summaryrefslogtreecommitdiff
path: root/tex/context/modules/mkiv
diff options
context:
space:
mode:
authorContext Git Mirror Bot <phg42.2a@gmail.com>2016-01-12 17:15:07 +0100
committerContext Git Mirror Bot <phg42.2a@gmail.com>2016-01-12 17:15:07 +0100
commit8d8d528d2ad52599f11250cfc567fea4f37f2a8b (patch)
tree94286bc131ef7d994f9432febaf03fe23d10eef8 /tex/context/modules/mkiv
parentf5aed2e51223c36c84c5f25a6cad238b2af59087 (diff)
downloadcontext-8d8d528d2ad52599f11250cfc567fea4f37f2a8b.tar.gz
2016-01-12 16:26:00
Diffstat (limited to 'tex/context/modules/mkiv')
-rw-r--r--tex/context/modules/mkiv/m-barcodes.mkiv122
-rw-r--r--tex/context/modules/mkiv/m-chart.lua945
-rw-r--r--tex/context/modules/mkiv/m-chart.mkvi540
-rw-r--r--tex/context/modules/mkiv/m-chemic.mkiv20
-rw-r--r--tex/context/modules/mkiv/m-cweb.mkiv1373
-rw-r--r--tex/context/modules/mkiv/m-database.lua132
-rw-r--r--tex/context/modules/mkiv/m-database.mkiv211
-rw-r--r--tex/context/modules/mkiv/m-directives.mkiv3
-rw-r--r--tex/context/modules/mkiv/m-educat.mkiv217
-rw-r--r--tex/context/modules/mkiv/m-escrito.lua7088
-rw-r--r--tex/context/modules/mkiv/m-escrito.mkiv184
-rw-r--r--tex/context/modules/mkiv/m-fields.mkiv70
-rw-r--r--tex/context/modules/mkiv/m-format.mkiv411
-rw-r--r--tex/context/modules/mkiv/m-graph.mkiv133
-rw-r--r--tex/context/modules/mkiv/m-hemistich.mkiv120
-rw-r--r--tex/context/modules/mkiv/m-ipsum.mkiv198
-rw-r--r--tex/context/modules/mkiv/m-json.mkiv30
-rw-r--r--tex/context/modules/mkiv/m-layout.mkiv102
-rw-r--r--tex/context/modules/mkiv/m-logcategories.mkiv3
-rw-r--r--tex/context/modules/mkiv/m-markdown.lua824
-rw-r--r--tex/context/modules/mkiv/m-markdown.mkiv88
-rw-r--r--tex/context/modules/mkiv/m-mathcrap.mkiv76
-rw-r--r--tex/context/modules/mkiv/m-matrix.mkiv495
-rw-r--r--tex/context/modules/mkiv/m-mkii.mkiv21
-rw-r--r--tex/context/modules/mkiv/m-mkivhacks.mkiv50
-rw-r--r--tex/context/modules/mkiv/m-morse.mkvi273
-rw-r--r--tex/context/modules/mkiv/m-narrowtt.mkiv39
-rw-r--r--tex/context/modules/mkiv/m-nodechart.lua177
-rw-r--r--tex/context/modules/mkiv/m-nodechart.mkvi125
-rw-r--r--tex/context/modules/mkiv/m-ntb-to-xtb.mkiv5
-rw-r--r--tex/context/modules/mkiv/m-obsolete.mkiv5
-rw-r--r--tex/context/modules/mkiv/m-oldfun.mkiv714
-rw-r--r--tex/context/modules/mkiv/m-oldnum.mkiv416
-rw-r--r--tex/context/modules/mkiv/m-pictex.mkiv46
-rw-r--r--tex/context/modules/mkiv/m-pipemode.mkiv7
-rw-r--r--tex/context/modules/mkiv/m-pstricks.lua74
-rw-r--r--tex/context/modules/mkiv/m-pstricks.mkiv65
-rw-r--r--tex/context/modules/mkiv/m-punk.mkiv257
-rw-r--r--tex/context/modules/mkiv/m-scite.mkiv279
-rw-r--r--tex/context/modules/mkiv/m-spreadsheet.lua332
-rw-r--r--tex/context/modules/mkiv/m-spreadsheet.mkiv221
-rw-r--r--tex/context/modules/mkiv/m-sql.mkiv29
-rw-r--r--tex/context/modules/mkiv/m-steps.lua226
-rw-r--r--tex/context/modules/mkiv/m-steps.mkvi382
-rw-r--r--tex/context/modules/mkiv/m-subsub.mkiv76
-rw-r--r--tex/context/modules/mkiv/m-timing.mkiv102
-rw-r--r--tex/context/modules/mkiv/m-trackers.mkiv3
-rw-r--r--tex/context/modules/mkiv/m-translate.mkiv127
-rw-r--r--tex/context/modules/mkiv/m-units.mkiv912
-rw-r--r--tex/context/modules/mkiv/m-visual.mkiv809
-rw-r--r--tex/context/modules/mkiv/m-zint.mkiv112
-rw-r--r--tex/context/modules/mkiv/ppchtex.mkiv3445
-rw-r--r--tex/context/modules/mkiv/s-art-01.mkiv62
-rw-r--r--tex/context/modules/mkiv/s-def-01.mkiv10
-rw-r--r--tex/context/modules/mkiv/s-figures-names.mkiv99
-rw-r--r--tex/context/modules/mkiv/s-fnt-10.mkiv169
-rw-r--r--tex/context/modules/mkiv/s-fnt-20.mkiv161
-rw-r--r--tex/context/modules/mkiv/s-fnt-21.mkiv64
-rw-r--r--tex/context/modules/mkiv/s-fnt-24.mkiv83
-rw-r--r--tex/context/modules/mkiv/s-fonts-charts.mkiv204
-rw-r--r--tex/context/modules/mkiv/s-fonts-coverage.lua123
-rw-r--r--tex/context/modules/mkiv/s-fonts-coverage.mkiv131
-rw-r--r--tex/context/modules/mkiv/s-fonts-features.lua161
-rw-r--r--tex/context/modules/mkiv/s-fonts-features.mkiv82
-rw-r--r--tex/context/modules/mkiv/s-fonts-goodies.lua117
-rw-r--r--tex/context/modules/mkiv/s-fonts-goodies.mkiv37
-rw-r--r--tex/context/modules/mkiv/s-fonts-ligatures.mkiv209
-rw-r--r--tex/context/modules/mkiv/s-fonts-missing.lua91
-rw-r--r--tex/context/modules/mkiv/s-fonts-missing.mkiv56
-rw-r--r--tex/context/modules/mkiv/s-fonts-shapes.lua326
-rw-r--r--tex/context/modules/mkiv/s-fonts-shapes.mkiv115
-rw-r--r--tex/context/modules/mkiv/s-fonts-system.lua68
-rw-r--r--tex/context/modules/mkiv/s-fonts-system.mkiv39
-rw-r--r--tex/context/modules/mkiv/s-fonts-tables.lua363
-rw-r--r--tex/context/modules/mkiv/s-fonts-tables.mkiv38
-rw-r--r--tex/context/modules/mkiv/s-fonts-vectors.lua104
-rw-r--r--tex/context/modules/mkiv/s-fonts-vectors.mkiv72
-rw-r--r--tex/context/modules/mkiv/s-inf-01.mkvi260
-rw-r--r--tex/context/modules/mkiv/s-inf-02.mkiv27
-rw-r--r--tex/context/modules/mkiv/s-inf-03.mkiv372
-rw-r--r--tex/context/modules/mkiv/s-inf-04.mkiv48
-rw-r--r--tex/context/modules/mkiv/s-languages-counters.lua52
-rw-r--r--tex/context/modules/mkiv/s-languages-counters.mkiv30
-rw-r--r--tex/context/modules/mkiv/s-languages-frequencies.lua33
-rw-r--r--tex/context/modules/mkiv/s-languages-frequencies.mkiv38
-rw-r--r--tex/context/modules/mkiv/s-languages-hyphenation.lua135
-rw-r--r--tex/context/modules/mkiv/s-languages-hyphenation.mkiv77
-rw-r--r--tex/context/modules/mkiv/s-languages-sorting.lua118
-rw-r--r--tex/context/modules/mkiv/s-languages-sorting.mkiv30
-rw-r--r--tex/context/modules/mkiv/s-languages-system.lua62
-rw-r--r--tex/context/modules/mkiv/s-languages-system.mkiv32
-rw-r--r--tex/context/modules/mkiv/s-languages-words.lua32
-rw-r--r--tex/context/modules/mkiv/s-languages-words.mkiv22
-rw-r--r--tex/context/modules/mkiv/s-map-10.mkiv494
-rw-r--r--tex/context/modules/mkiv/s-math-characters.lua242
-rw-r--r--tex/context/modules/mkiv/s-math-characters.mkiv193
-rw-r--r--tex/context/modules/mkiv/s-math-coverage.lua197
-rw-r--r--tex/context/modules/mkiv/s-math-coverage.mkiv38
-rw-r--r--tex/context/modules/mkiv/s-math-extensibles.mkiv145
-rw-r--r--tex/context/modules/mkiv/s-math-parameters.lua135
-rw-r--r--tex/context/modules/mkiv/s-math-parameters.mkiv41
-rw-r--r--tex/context/modules/mkiv/s-math-repertoire.mkiv487
-rw-r--r--tex/context/modules/mkiv/s-mod-00.mkiv24
-rw-r--r--tex/context/modules/mkiv/s-mod-01.mkiv390
-rw-r--r--tex/context/modules/mkiv/s-mod-02.mkiv24
-rw-r--r--tex/context/modules/mkiv/s-pages-statistics.mkiv134
-rw-r--r--tex/context/modules/mkiv/s-physics-units.mkiv30
-rw-r--r--tex/context/modules/mkiv/s-pre-17.mkiv408
-rw-r--r--tex/context/modules/mkiv/s-pre-30.mkiv257
-rw-r--r--tex/context/modules/mkiv/s-pre-60.mkiv212
-rw-r--r--tex/context/modules/mkiv/s-pre-69.mkiv336
-rw-r--r--tex/context/modules/mkiv/s-pre-70.mkiv176
-rw-r--r--tex/context/modules/mkiv/s-pre-71.lua63
-rw-r--r--tex/context/modules/mkiv/s-pre-71.mkiv170
-rw-r--r--tex/context/modules/mkiv/s-present-tiles.mkiv318
-rw-r--r--tex/context/modules/mkiv/s-references-show.mkiv132
-rw-r--r--tex/context/modules/mkiv/s-reg-01.mkiv60
-rw-r--r--tex/context/modules/mkiv/s-set-31.mkiv118
-rw-r--r--tex/context/modules/mkiv/s-sql-tables.lua152
-rw-r--r--tex/context/modules/mkiv/s-sql-tables.mkiv30
-rw-r--r--tex/context/modules/mkiv/s-structure-sections.mkiv80
-rw-r--r--tex/context/modules/mkiv/s-syntax.mkiv96
-rw-r--r--tex/context/modules/mkiv/s-typesetting-kerning.mkiv209
-rw-r--r--tex/context/modules/mkiv/s-youless.mkiv171
-rw-r--r--tex/context/modules/mkiv/x-asciimath.lua2209
-rw-r--r--tex/context/modules/mkiv/x-asciimath.mkiv436
-rw-r--r--tex/context/modules/mkiv/x-calcmath.lua363
-rw-r--r--tex/context/modules/mkiv/x-calcmath.mkiv80
-rw-r--r--tex/context/modules/mkiv/x-cals.lua221
-rw-r--r--tex/context/modules/mkiv/x-cals.mkiv45
-rw-r--r--tex/context/modules/mkiv/x-chemml.lua51
-rw-r--r--tex/context/modules/mkiv/x-chemml.mkiv228
-rw-r--r--tex/context/modules/mkiv/x-ct.lua167
-rw-r--r--tex/context/modules/mkiv/x-ct.mkiv29
-rw-r--r--tex/context/modules/mkiv/x-entities.mkiv18
-rw-r--r--tex/context/modules/mkiv/x-foxet.mkiv29
-rw-r--r--tex/context/modules/mkiv/x-html.mkiv379
-rw-r--r--tex/context/modules/mkiv/x-ldx.ctx23
-rw-r--r--tex/context/modules/mkiv/x-ldx.lua341
-rw-r--r--tex/context/modules/mkiv/x-ldx.mkiv196
-rw-r--r--tex/context/modules/mkiv/x-math-svg.lua176
-rw-r--r--tex/context/modules/mkiv/x-mathml-basics.mkiv276
-rw-r--r--tex/context/modules/mkiv/x-mathml-html.mkiv40
-rw-r--r--tex/context/modules/mkiv/x-mathml.lua898
-rw-r--r--tex/context/modules/mkiv/x-mathml.mkiv2605
-rw-r--r--tex/context/modules/mkiv/x-newmml.mkiv16
-rw-r--r--tex/context/modules/mkiv/x-pfs-01.mkiv399
-rw-r--r--tex/context/modules/mkiv/x-pfsense.ctx15
-rw-r--r--tex/context/modules/mkiv/x-physml.mkiv16
-rw-r--r--tex/context/modules/mkiv/x-res-01.mkiv427
-rw-r--r--tex/context/modules/mkiv/x-res-50.mkiv431
-rw-r--r--tex/context/modules/mkiv/x-set-11.mkiv859
-rw-r--r--tex/context/modules/mkiv/x-set-12.mkiv267
-rw-r--r--tex/context/modules/mkiv/x-steps.mkiv102
-rw-r--r--tex/context/modules/mkiv/x-udhr.mkiv98
-rw-r--r--tex/context/modules/mkiv/x-xfdf.mkiv72
156 files changed, 44140 insertions, 0 deletions
diff --git a/tex/context/modules/mkiv/m-barcodes.mkiv b/tex/context/modules/mkiv/m-barcodes.mkiv
new file mode 100644
index 000000000..e4c43b376
--- /dev/null
+++ b/tex/context/modules/mkiv/m-barcodes.mkiv
@@ -0,0 +1,122 @@
+%D \module
+%D [ file=m-barcodes,
+%D version=2010.03.14,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=Barcodes,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{barcodes}{the 'zint' module is a better choice}
+
+% \startTEXpage
+% \startPSTRICKS
+% \pspicture(-4mm,-1mm)(38mm,26mm)
+% \psbarcode{9781860742712}{includetext guardwhitespace}{ean13}%
+% \endpspicture
+% \stopPSTRICKS
+% \stopTEXpage
+
+% 978-94-90688-01-1
+%
+% 978 = ean isbn identifier (979 also)
+% 94 = country code
+% 90688 = publisher code
+% 01 = title 1
+% 1 = checksum
+
+\usemodule[pstricks]
+
+\usePSTRICKSmodule[pst-barcode]
+
+\definefont[barcodefont][file:ocrb10]
+% \definefont[barcodefont][file:texgyreheros-regular]
+
+\startluacode
+moduledata.barcodes = { }
+
+local function split(code)
+ local t = { string.byte(code,1,#code) }
+ if #t >= 12 then
+ local s = 0
+ for i=1,11,2 do
+ s = s + (t[i]-48)
+ end
+ for i=2,12,2 do
+ s = s + 3 * (t[i]-48)
+ end
+ local m = s % 10
+ local c = (m > 0 and (10 - m)) or 0
+ return t, s, m, c
+ end
+end
+
+function moduledata.barcodes.isbn_1(original)
+ local code = string.gsub(original,"%-","")
+ local t, s, m, c = split(code)
+ if t then
+ if #t == 13 then
+ local e = ((c == t[13] - 48) and "correct") or "wrong"
+ logs.report("isbn code","code=%s, sum=%s, checksum=%s, modulo=%s, status=%s",original,s,m,c,e)
+ else
+ logs.report("isbn code","code=%s, sum=%s, checksum=%s, modulo=%s",original,s,m,c)
+ code= code .. c
+ end
+ end
+ context(code)
+end
+
+function moduledata.barcodes.isbn_2(original)
+ local code = string.gsub(original,"%-","")
+ local t, s, m, c = split(code)
+ if t and #t == 12 then
+ original = original .. "-" .. c
+ end
+ context(original)
+end
+\stopluacode
+
+\startsetups barcode:isbn
+ \scale
+ [width=5cm]
+ {
+ \vbox {
+ \hbox {
+ \hskip3.7mm
+ \scale[width=34mm]{\barcodefont ISBN \ctxlua{moduledata.barcodes.isbn_2("\getvariable{barcode}{code}")}}
+ }
+ \par
+ \normalexpanded { \noexpand \setPSTRICKS {
+ \noexpand \pspicture(-4mm,-1mm)(38mm,26mm)
+ \noexpand \psbarcode {
+ \ctxlua{moduledata.barcodes.isbn_1("\getvariable{barcode}{code}")}
+ } {
+ includetext guardwhitespace
+ } {
+ ean13
+ }
+ \noexpand \endpspicture
+ }
+ \noexpand \processPSTRICKS }
+ }
+ }
+\stopsetups
+
+\unexpanded\def\barcode[#1]%
+ {\bgroup
+ \setvariables[barcode][type=isbn,#1]%
+ \directsetup{barcode:\getvariable{barcode}{type}}%
+ \egroup}
+
+\continueifinputfile{m-barcodes.mkiv}
+
+\starttext
+ \startTEXpage
+ \barcode[type=isbn,code=978-94-90688-01-1]
+ \stopTEXpage
+\stoptext
+
diff --git a/tex/context/modules/mkiv/m-chart.lua b/tex/context/modules/mkiv/m-chart.lua
new file mode 100644
index 000000000..f1e7f4cb9
--- /dev/null
+++ b/tex/context/modules/mkiv/m-chart.lua
@@ -0,0 +1,945 @@
+if not modules then modules = { } end modules ['x-flow'] = {
+ version = 1.001,
+ comment = "companion to m-flow.mkvi",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- when we can resolve mpcolor at the lua end we will
+-- use metapost.graphic(....) directly
+
+-- todo: labels
+
+moduledata.charts = moduledata.charts or { }
+
+local gsub, match, find, format, lower = string.gsub, string.match, string.find, string.format, string.lower
+local setmetatableindex = table.setmetatableindex
+local P, S, C, Cc, lpegmatch = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc, lpeg.match
+
+local report_chart = logs.reporter("chart")
+
+local variables = interfaces.variables
+
+local v_yes = variables.yes
+local v_no = variables.no
+local v_none = variables.none
+local v_standard = variables.standard
+local v_overlay = variables.overlay
+local v_round = variables.round
+local v_test = variables.test
+
+local defaults = {
+ chart = {
+ name = "",
+ option = "",
+ backgroundcolor = "",
+ width = 100*65536,
+ height = 50*65536,
+ dx = 30*65536,
+ dy = 30*65536,
+ offset = 0,
+ bodyfont = "",
+ dot = "",
+ hcompact = variables_no,
+ vcompact = variables_no,
+ autofocus = "",
+ focus = "",
+ labeloffset = 5*65536,
+ commentoffset = 5*65536,
+ exitoffset = 0,
+
+ },
+ shape = { -- FLOS
+ rulethickness = 65536,
+ default = "",
+ framecolor = "darkblue",
+ backgroundcolor = "lightgray",
+ },
+ focus = { -- FLOF
+ rulethickness = 65536,
+ framecolor = "darkred",
+ backgroundcolor = "gray",
+ },
+ line = { -- FLOL
+ rulethickness = 65536,
+ radius = 10*65536,
+ color = "darkgreen",
+ corner = "",
+ dash = "",
+ arrow = "",
+ offset = "",
+ },
+ set = { -- FLOX
+ },
+ split = {
+ nx = 3,
+ ny = 3,
+ command = "",
+ marking = "",
+ before = "",
+ after = "",
+ }
+}
+
+local validshapes = {
+ ["node"] = { kind = "shape", number = 0 },
+ ["action"] = { kind = "shape", number = 24 },
+ ["procedure"] = { kind = "shape", number = 5 },
+ ["product"] = { kind = "shape", number = 12 },
+ ["decision"] = { kind = "shape", number = 14 },
+ ["archive"] = { kind = "shape", number = 19 },
+ ["loop"] = { kind = "shape", number = 35 },
+ ["wait"] = { kind = "shape", number = 6 },
+ ["subprocedure"] = { kind = "shape", number = 20 },
+ ["singledocument"] = { kind = "shape", number = 32 },
+ ["multidocument"] = { kind = "shape", number = 33 },
+
+ ["right"] = { kind = "line", number = 66 },
+ ["left"] = { kind = "line", number = 67 },
+ ["up"] = { kind = "line", number = 68 },
+ ["down"] = { kind = "line", number = 69 },
+}
+
+local validlabellocations = {
+ l = "l", left = "l",
+ r = "r", right = "r",
+ t = "t", top = "t",
+ b = "b", bottom = "b",
+ lt = "lt",
+ rt = "rt",
+ lb = "lb",
+ rb = "rb",
+ tl = "tl",
+ tr = "tr",
+ bl = "bl",
+ br = "br",
+}
+
+local validcommentlocations = {
+ l = "l", left = "l",
+ r = "r", right = "r",
+ t = "t", top = "t",
+ b = "b", bottom = "b",
+ lt = "lt",
+ rt = "rt",
+ lb = "lb",
+ rb = "rb",
+ tl = "tl",
+ tr = "tr",
+ bl = "bl",
+ br = "br",
+}
+
+local validtextlocations = {
+ l = "l", left = "l",
+ r = "r", right = "r",
+ t = "t", top = "t",
+ b = "b", bottom = "b",
+ c = "c", center = "c",
+ m = "c", middle = "m",
+ lt = "lt",
+ rt = "rt",
+ lb = "lb",
+ rb = "rb",
+ tl = "lt",
+ tr = "rt",
+ bl = "lb",
+ br = "rb",
+}
+
+setmetatableindex(validshapes,function(t,k)
+ local l = gsub(lower(k)," ","")
+ local v = rawget(t,l)
+ if not v then
+ local n = tonumber(k)
+ if n then
+ v = { kind = "shape", number = n }
+ else
+ v = rawget(t,"action")
+ end
+ end
+ t[k] = v
+ return v
+end)
+
+local charts = { }
+
+local data, hash, temp, last_x, last_y, name
+
+function commands.flow_start_chart(chartname)
+ data = { }
+ hash = { }
+ last_x, last_y = 0, 0
+ name = chartname
+end
+
+function commands.flow_stop_chart()
+ charts[name] = {
+ data = data,
+ hash = hash,
+ last_x = last_x,
+ last_y = last_y,
+ }
+ data, hash, temp = nil, nil, nil
+end
+
+-- function commands.flow_set(chartname,chartdata)
+-- local hash = { }
+-- local data = { }
+-- charts[name] = {
+-- data = data,
+-- hash = hash,
+-- }
+-- for i=1,#chartdata do
+-- local di = data[i]
+-- local name = di.name or ""
+-- if name then
+-- data[#data+1] = {
+-- name = name,
+-- labels = di.labels or { },
+-- comments = di.comments or { },
+-- exits = di.exits or { },
+-- connections = di.connections or { },
+-- settings = di.settings or { },
+-- x = di.x or 1,
+-- y = di.y or 1,
+-- }
+-- hash[name] = i
+-- end
+-- end
+-- end
+
+function commands.flow_reset(chartname)
+ charts[name] = nil
+end
+
+function commands.flow_set_current_cell(n)
+ temp = data[tonumber(n)] or { }
+end
+
+function commands.flow_start_cell(settings)
+ temp = {
+ texts = { },
+ labels = { },
+ exits = { },
+ connections = { },
+ settings = settings,
+ x = 1,
+ y = 1,
+ realx = 1,
+ realy = 1,
+ name = "",
+ }
+end
+
+function commands.flow_stop_cell()
+ data[#data+1] = temp
+ hash[temp.name or #data] = temp
+end
+
+function commands.flow_set_name(str)
+ temp.name = str
+end
+
+function commands.flow_set_shape(str)
+ temp.shape = str
+end
+
+function commands.flow_set_destination(str)
+ temp.destination = str
+end
+
+function commands.flow_set_text(align,str)
+ temp.texts[#temp.texts+1] = {
+ location = align,
+ text = str,
+ }
+end
+
+function commands.flow_set_overlay(str)
+ temp.overlay = str
+end
+
+function commands.flow_set_focus(str)
+ temp.focus = str
+end
+
+function commands.flow_set_figure(str)
+ temp.figure = str
+end
+
+function commands.flow_set_label(location,text)
+ temp.labels[#temp.labels+1] = {
+ location = location,
+ text = text,
+ }
+end
+
+function commands.flow_set_comment(location,text)
+ local connections = temp.connections
+ if connections then
+ local connection = connections[#connections]
+ if connection then
+ local comments = connection.comments
+ if comments then
+ comments[#comments+1] = {
+ location = location,
+ text = text,
+ }
+ end
+ end
+ end
+end
+
+function commands.flow_set_exit(location,text)
+ temp.exits[#temp.exits+1] = {
+ location = location,
+ text = text,
+ }
+end
+
+function commands.flow_set_include(name,x,y,settings)
+ data[#data+1] = {
+ include = name,
+ x = x,
+ y = y,
+ -- settings = settings,
+ }
+end
+
+local function inject(includedata,data,hash)
+ local subchart = charts[includedata.include]
+ if not subchart then
+ return
+ end
+ local subdata = subchart.data
+ if not subdata then
+ return
+ end
+ local xoffset = (includedata.x or 1) - 1
+ local yoffset = (includedata.y or 1) - 1
+ local settings = includedata.settings
+ for i=1,#subdata do
+ local si = subdata[i]
+ if si.include then
+ inject(si,data,hash)
+ else
+ local x = si.x + xoffset
+ local y = si.y + yoffset
+ local t = {
+ x = x,
+ y = y,
+ realx = x,
+ realy = y,
+ settings = settings,
+ }
+ setmetatableindex(t,si)
+ data[#data+1] = t
+ hash[si.name or #data] = t
+ end
+ end
+end
+
+local function pack(data,field)
+ local list, max = { }, 0
+ for e=1,#data do
+ local d = data[e]
+ local f = d[field]
+ list[f] = true
+ if f > max then
+ max = f
+ end
+ end
+ for i=1,max do
+ if not list[i] then
+ for e=1,#data do
+ local d = data[e]
+ local f = d[field]
+ if f > i then
+ d[field] = f - 1
+ end
+ end
+ end
+ end
+end
+
+local function expanded(chart,chartsettings)
+ local expandeddata = { }
+ local expandedhash = { }
+ local expandedchart = {
+ data = expandeddata,
+ hash = expandedhash,
+ }
+ setmetatableindex(expandedchart,chart)
+ local data = chart.data
+ local hash = chart.hash
+ for i=1,#data do
+ local di = data[i]
+ if di.include then
+ inject(di,expandeddata,expandedhash)
+ else
+ expandeddata[#expandeddata+1] = di
+ expandedhash[di.name or #expandeddata] = di
+ end
+ end
+ --
+ expandedchart.settings = chartsettings or { }
+ -- make locals
+ chartsettings.shape = chartsettings.shape or { }
+ chartsettings.focus = chartsettings.focus or { }
+ chartsettings.line = chartsettings.line or { }
+ chartsettings.set = chartsettings.set or { }
+ chartsettings.split = chartsettings.split or { }
+ chartsettings.chart = chartsettings.chart or { }
+ setmetatableindex(chartsettings.shape,defaults.shape)
+ setmetatableindex(chartsettings.focus,defaults.focus)
+ setmetatableindex(chartsettings.line ,defaults.line )
+ setmetatableindex(chartsettings.set ,defaults.set )
+ setmetatableindex(chartsettings.split,defaults.split)
+ setmetatableindex(chartsettings.chart,defaults.chart)
+ --
+ if chartsettings.chart.vcompact == v_yes then
+ pack(expandeddata,"y")
+ end
+ if chartsettings.chart.hcompact == v_yes then
+ pack(expandeddata,"x")
+ end
+ --
+ for i=1,#expandeddata do
+ local cell = expandeddata[i]
+ local settings = cell.settings
+ if not settings then
+ cell.settings = chartsettings
+ else
+ settings.shape = settings.shape or { }
+ settings.focus = settings.focus or { }
+ settings.line = settings.line or { }
+ setmetatableindex(settings.shape,chartsettings.shape)
+ setmetatableindex(settings.focus,chartsettings.focus)
+ setmetatableindex(settings.line ,chartsettings.line)
+ end
+ end
+ return expandedchart
+end
+
+local splitter = lpeg.splitat(",")
+
+function commands.flow_set_location(x,y)
+ if type(x) == "string" and not y then
+ x, y = lpegmatch(splitter,x)
+ end
+ if not x or x == "" then
+ x = last_x
+ elseif type(x) == "number" then
+ -- ok
+ elseif x == "+" then
+ x = last_x + 1
+ elseif x == "-" then
+ x = last_x - 1
+ elseif find(x,"^[%+%-]") then
+ x = last_x + (tonumber(x) or 0)
+ else
+ x = tonumber(x)
+ end
+ if not y or y == "" then
+ y = last_y
+ elseif type(y) == "number" then
+ -- ok
+ elseif y == "+" then
+ y = last_y + 1
+ elseif x == "-" then
+ y = last_y - 1
+ elseif find(y,"^[%+%-]") then
+ y = last_y + (tonumber(y) or 0)
+ else
+ y = tonumber(y)
+ end
+ temp.x = x or 1
+ temp.y = y or 1
+ temp.realx = x or 1
+ temp.realy = y or 1
+ last_x = x or last_x
+ last_y = y or last_y
+end
+
+function commands.flow_set_connection(location,displacement,name)
+ local dx, dy = lpegmatch(splitter,displacement)
+ dx = tonumber(dx)
+ dy = tonumber(dy)
+ temp.connections[#temp.connections+1] = {
+ location = location,
+ dx = dx or 0,
+ dy = dy or 0,
+ name = name,
+ comments = { },
+ }
+end
+
+local function visible(chart,cell)
+ local x, y = cell.x, cell.y
+ return
+ x >= chart.from_x and x <= chart.to_x and
+ y >= chart.from_y and y <= chart.to_y and cell
+end
+
+local function process_cells(chart,xoffset,yoffset)
+ local data = chart.data
+ if not data then
+ return
+ end
+ local focus = utilities.parsers.settings_to_hash(chart.settings.chart.focus or "")
+ for i=1,#data do
+ local cell = visible(chart,data[i])
+ if cell then
+ local settings = cell.settings
+ local shapesettings = settings.shape
+ local shape = cell.shape
+ if not shape or shape == "" then
+ shape = shapesettings.default or "none"
+ end
+ if shape ~= v_none then
+ local shapedata = validshapes[shape]
+ context("flow_begin_sub_chart ;") -- when is this needed
+ if shapedata.kind == "line" then
+ local linesettings = settings.line
+ context("flow_shape_line_color := \\MPcolor{%s} ;", linesettings.color)
+ context("flow_shape_fill_color := \\MPcolor{%s} ;", linesettings.backgroundcolor)
+ context("flow_shape_line_width := %p ; ", linesettings.rulethickness)
+ elseif focus[cell.focus] or focus[cell.name] then
+ local focussettings = settings.focus
+ context("flow_shape_line_color := \\MPcolor{%s} ;", focussettings.framecolor)
+ context("flow_shape_fill_color := \\MPcolor{%s} ;", focussettings.backgroundcolor)
+ context("flow_shape_line_width := %p ; ", focussettings.rulethickness)
+ else
+ local shapesettings = settings.shape
+ context("flow_shape_line_color := \\MPcolor{%s} ;", shapesettings.framecolor)
+ context("flow_shape_fill_color := \\MPcolor{%s} ;", shapesettings.backgroundcolor)
+ context("flow_shape_line_width := %p ; " , shapesettings.rulethickness)
+ end
+ context("flow_peepshape := false ;") -- todo
+ context("flow_new_shape(%s,%s,%s) ;",cell.x+xoffset,cell.y+yoffset,shapedata.number)
+ context("flow_end_sub_chart ;")
+ end
+ end
+ end
+end
+
+-- todo : make lpeg for splitter
+
+local sign = S("+p") / "1"
+ + S("-m") / "-1"
+
+local full = C(P("left"))
+ + C(P("right"))
+ + C(P("top"))
+ + C(P("bottom"))
+
+local char = P("l") / "left"
+ + P("r") / "right"
+ + P("t") / "top"
+ + P("b") / "bottom"
+
+local space = P(" ")^0
+
+local what = space
+ * (sign + Cc("0"))
+ * space
+ * (full + char)
+ * space
+ * (sign + Cc("0"))
+ * space
+ * (full + char)
+ * space
+ * P(-1)
+
+-- print(lpegmatch(what,"lr"))
+-- print(lpegmatch(what,"+l+r"))
+-- print(lpegmatch(what,"+l"))
+-- print(lpegmatch(what,"+ left+r "))
+
+local function process_connections(chart,xoffset,yoffset)
+ local data = chart.data
+ local hash = chart.hash
+ if not data then
+ return
+ end
+ local settings = chart.settings
+ for i=1,#data do
+ local cell = visible(chart,data[i])
+ if cell then
+ local connections = cell.connections
+ for j=1,#connections do
+ local connection = connections[j]
+ local othername = connection.name
+ local othercell = hash[othername]
+ if othercell then -- and visible(chart,data[i]) then
+ local cellx, celly = cell.x, cell.y
+ local otherx, othery, location = othercell.x, othercell.y, connection.location
+ if otherx > 0 and othery > 0 and cellx > 0 and celly > 0 and connection.location then
+ local what_cell, where_cell, what_other, where_other = lpegmatch(what,location)
+ if what_cell and where_cell and what_other and where_other then
+ local linesettings = settings.line
+ context("flow_smooth := %s ;", linesettings.corner == v_round and "true" or "false")
+ context("flow_dashline := %s ;", linesettings.dash == v_yes and "true" or "false")
+ context("flow_arrowtip := %s ;", linesettings.arrow == v_yes and "true" or "false")
+ context("flow_touchshape := %s ;", linesettings.offset == v_none and "true" or "false")
+ context("flow_dsp_x := %s ; flow_dsp_y := %s ;",connection.dx or 0, connection.dy or 0)
+ context("flow_connection_line_color := \\MPcolor{%s} ;",linesettings.color)
+ context("flow_connection_line_width := %p ;",linesettings.rulethickness)
+ context("flow_connect_%s_%s (%s) (%s,%s,%s) (%s,%s,%s) ;",where_cell,where_other,j,cellx,celly,what_cell,otherx,othery,what_other)
+ context("flow_dsp_x := 0 ; flow_dsp_y := 0 ;")
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+local texttemplate = "\\setvariables[flowcell:text][x=%s,y=%s,text={%s},align={%s},figure={%s},destination={%s}]"
+
+local splitter = lpeg.splitat(":")
+
+local function process_texts(chart,xoffset,yoffset)
+ local data = chart.data
+ local hash = chart.hash
+ if not data then
+ return
+ end
+ for i=1,#data do
+ local cell = visible(chart,data[i])
+ if cell then
+ local x = cell.x or 1
+ local y = cell.y or 1
+ local texts = cell.texts
+ for i=1,#texts do
+ local text = texts[i]
+ local data = text.text
+ local align = validlabellocations[text.align or ""] or text.align or ""
+ local figure = i == 1 and cell.figure or ""
+ local destination = i == 1 and cell.destination or ""
+ context('flow_chart_draw_text(%s,%s,textext("%s")) ;',x,y,format(texttemplate,x,y,data,align,figure,destination))
+ end
+ local labels = cell.labels
+ for i=1,#labels do
+ local label = labels[i]
+ local text = label.text
+ local location = validlabellocations[label.location or ""] or label.location or ""
+ if text and location then
+ context('flow_chart_draw_label(%s,%s,"%s",textext("\\strut %s")) ;',x,y,location,text)
+ end
+ end
+ local exits = cell.exits
+ for i=1,#exits do
+ local exit = exits[i]
+ local text = exit.text
+ local location = validlabellocations[exit.location or ""]
+ if text and location then
+ -- maybe make autoexit an option
+ if location == "l" and x == chart.from_x + 1 or
+ location == "r" and x == chart.to_x - 1 or
+ location == "t" and y == chart.to_y - 1 or
+ location == "b" and y == chart.from_y + 1 then
+ context('flow_chart_draw_exit(%s,%s,"%s",textext("\\strut %s")) ;',x,y,location,text)
+ end
+ end
+ end
+ local connections = cell.connections
+ for i=1,#connections do
+ local comments = connections[i].comments
+ for j=1,#comments do
+ local comment = comments[j]
+ local text = comment.text
+ local location = comment.location or ""
+ local length = 0
+ -- "tl" "tl:*" "tl:0.5"
+ local loc, len = lpegmatch(splitter,location) -- do the following in lpeg
+ if len == "*" then
+ location = validcommentlocations[loc] or ""
+ if location == "" then
+ location = "*"
+ else
+ location = location .. ":*"
+ end
+ elseif loc then
+ location = validcommentlocations[loc] or "*"
+ length = tonumber(len) or 0
+ else
+ location = validcommentlocations[location] or ""
+ end
+ if text and location then
+ context('flow_chart_draw_comment(%s,%s,%s,"%s",%s,textext("\\strut %s")) ;',x,y,i,location,length,text)
+ end
+ end
+ end
+ end
+ end
+end
+
+local function getchart(settings,forced_x,forced_y,forced_nx,forced_ny)
+ if not settings then
+ print("no settings given")
+ return
+ end
+ local chartname = settings.chart.name
+ if not chartname then
+ print("no name given")
+ return
+ end
+ local chart = charts[chartname]
+ if not chart then
+ print("no such chart",chartname)
+ return
+ end
+-- chart = table.copy(chart)
+ chart = expanded(chart,settings)
+ local chartsettings = chart.settings.chart
+ local autofocus = chart.settings.chart.autofocus
+ if autofocus then
+ autofocus = utilities.parsers.settings_to_hash(autofocus)
+ if not next(autofocus) then
+ autofocus = false
+ end
+ end
+ -- check natural window
+ local x = forced_x or tonumber(chartsettings.x)
+ local y = forced_y or tonumber(chartsettings.y)
+ local nx = forced_nx or tonumber(chartsettings.nx)
+ local ny = forced_ny or tonumber(chartsettings.ny)
+ --
+ local minx, miny, maxx, maxy = 0, 0, 0, 0
+ local data = chart.data
+ for i=1,#data do
+ local cell = data[i]
+ if not autofocus or autofocus[cell.name] then -- offsets probably interfere with autofocus
+ local x = cell.x
+ local y = cell.y
+ if minx == 0 or x < minx then minx = x end
+ if miny == 0 or y < miny then miny = y end
+ if minx == 0 or x > maxx then maxx = x end
+ if miny == 0 or y > maxy then maxy = y end
+ end
+ end
+ -- print("1>",x,y,nx,ny)
+ -- print("2>",minx, miny, maxx, maxy)
+ -- check of window should be larger (maybe autofocus + nx/ny?)
+ if autofocus then
+ -- x and y are ignored
+ if nx and nx > 0 then
+ maxx = minx + nx - 1
+ end
+ if ny and ny > 0 then
+ maxy = miny + ny - 1
+ end
+ else
+ if x and x > 0 then
+ minx = x
+ end
+ if y and y > 0 then
+ miny = y
+ end
+ if nx and nx > 0 then
+ maxx = minx + nx - 1
+ end
+ if ny and ny > 0 then
+ maxy = miny + ny - 1
+ end
+ end
+-- print("3>",minx, miny, maxx, maxy)
+ --
+ local nx = maxx - minx + 1
+ local ny = maxy - miny + 1
+ -- relocate cells
+ for i=1,#data do
+ local cell = data[i]
+ cell.x = cell.realx - minx + 1
+ cell.y = cell.realy - miny + 1
+ end
+ chart.from_x = 1
+ chart.from_y = 1
+ chart.to_x = nx
+ chart.to_y = ny
+ chart.nx = nx
+ chart.ny = ny
+ --
+ chart.shift_x = minx + 1
+ chart.shift_y = miny + 1
+ --
+ return chart
+end
+
+local function makechart(chart)
+ local settings = chart.settings
+ local chartsettings = settings.chart
+ --
+ context.begingroup()
+ context.forgetall()
+ --
+ context.startMPcode()
+ context("if unknown context_flow : input mp-char.mpiv ; fi ;")
+ context("flow_begin_chart(0,%s,%s);",chart.nx,chart.ny)
+ --
+ if chartsettings.option == v_test or chartsettings.dot == v_yes then
+ context("flow_show_con_points := true ;")
+ context("flow_show_mid_points := true ;")
+ context("flow_show_all_points := true ;")
+ elseif chartsettings.dot ~= "" then -- no checking done, private option
+ context("flow_show_%s_points := true ;",chartsettings.dot)
+ end
+ --
+ local backgroundcolor = chartsettings.backgroundcolor
+ if backgroundcolor and backgroundcolor ~= "" then
+ context("flow_chart_background_color := \\MPcolor{%s} ;",backgroundcolor)
+ end
+ --
+ local shapewidth = chartsettings.width
+ local gridwidth = shapewidth + 2*chartsettings.dx
+ local shapeheight = chartsettings.height
+ local gridheight = shapeheight + 2*chartsettings.dy
+ local chartoffset = chartsettings.offset
+ local labeloffset = chartsettings.labeloffset
+ local exitoffset = chartsettings.exitoffset
+ local commentoffset = chartsettings.commentoffset
+ context("flow_grid_width := %p ;", gridwidth)
+ context("flow_grid_height := %p ;", gridheight)
+ context("flow_shape_width := %p ;", shapewidth)
+ context("flow_shape_height := %p ;", shapeheight)
+ context("flow_chart_offset := %p ;", chartoffset)
+ context("flow_label_offset := %p ;", labeloffset)
+ context("flow_exit_offset := %p ;", exitoffset)
+ context("flow_comment_offset := %p ;", commentoffset)
+ --
+ local radius = settings.line.radius
+ local rulethickness = settings.line.rulethickness
+ local dx = chartsettings.dx
+ local dy = chartsettings.dy
+ if radius < rulethickness then
+ radius = 2.5*rulethickness
+ if radius > dx then
+ radius = dx
+ end
+ if radius > dy then
+ radius = dy
+ end
+ end
+ context("flow_connection_line_width := %p ;", rulethickness)
+ context("flow_connection_smooth_size := %p ;", radius)
+ context("flow_connection_arrow_size := %p ;", radius)
+ context("flow_connection_dash_size := %p ;", radius)
+ --
+ local offset = chartsettings.offset -- todo: pass string
+ if offset == v_none or offset == v_overlay or offset == "" then
+ offset = -2.5 * radius -- or rulethickness?
+ elseif offset == v_standard then
+ offset = radius -- or rulethickness?
+ end
+ context("flow_chart_offset := %p ;",offset)
+ --
+ context("flow_reverse_y := true ;")
+ process_cells(chart,0,0)
+ process_connections(chart,0,0)
+ process_texts(chart,0,0)
+ -- context("clip_chart(%s,%s,%s,%s) ;",x,y,nx,ny) -- todo: draw lines but not shapes
+ context("flow_end_chart ;")
+ context.stopMPcode()
+ context.endgroup()
+end
+
+local function splitchart(chart)
+ local settings = chart.settings
+ local splitsettings = settings.split
+ local chartsettings = settings.chart
+ --
+ local name = chartsettings.name
+ --
+ local from_x = chart.from_x
+ local from_y = chart.from_y
+ local to_x = chart.to_x
+ local to_y = chart.to_y
+ --
+ local step_x = splitsettings.nx or to_x
+ local step_y = splitsettings.ny or to_y
+ local delta_x = splitsettings.dx or 0
+ local delta_y = splitsettings.dy or 0
+ --
+ report_chart("spliting %a from (%s,%s) upto (%s,%s) with steps (%s,%s) and overlap (%s,%s)",
+ name,from_x,from_y,to_x,to_y,step_x,step_y,delta_x,delta_y)
+ --
+ local part_x = 0
+ local first_x = from_x
+ while true do
+ part_x = part_x + 1
+ local last_x = first_x + step_x - 1
+ local done = last_x >= to_x
+ if done then
+ last_x = to_x
+ end
+-- if first_x >= to_x then
+-- break
+-- end
+ local part_y = 0
+ local first_y = from_y
+ while true do
+ part_y = part_y + 1
+ local last_y = first_y + step_y - 1
+ local done = last_y >= to_y
+ if done then
+ last_y = to_y
+ end
+-- if first_y >= to_y then
+-- break
+-- end
+ --
+local data = chart.data
+for i=1,#data do
+ local cell = data[i]
+-- inspect(cell)
+ local cx, cy = cell.x, cell.y
+ if cx >= first_x and cx <= last_x then
+ if cy >= first_y and cy <= last_y then
+ report_chart("part (%s,%s) of %a is split from (%s,%s) -> (%s,%s)",part_x,part_y,name,first_x,first_y,last_x,last_y)
+ local x = first_x
+ local y = first_y
+ local nx = last_x - first_x + 1
+ local ny = last_y - first_y + 1
+ context.beforeFLOWsplit()
+ context.handleFLOWsplit(function()
+ makechart(getchart(settings,x,y,nx,ny)) -- we need to pass frozen settings !
+ end)
+ context.afterFLOWsplit()
+ break
+ end
+ end
+end
+ --
+ if done then
+ break
+ else
+ first_y = last_y + 1 - delta_y
+ end
+ end
+ if done then
+ break
+ else
+ first_x = last_x + 1 - delta_x
+ end
+ end
+end
+
+function commands.flow_make_chart(settings)
+ local chart = getchart(settings)
+ if chart then
+ local settings = chart.settings
+ if settings then
+ local chartsettings = settings.chart
+ if chartsettings and chartsettings.split == v_yes then
+ splitchart(chart)
+ else
+ makechart(chart)
+ end
+ else
+ makechart(chart)
+ end
+ end
+end
diff --git a/tex/context/modules/mkiv/m-chart.mkvi b/tex/context/modules/mkiv/m-chart.mkvi
new file mode 100644
index 000000000..a0c8b2244
--- /dev/null
+++ b/tex/context/modules/mkiv/m-chart.mkvi
@@ -0,0 +1,540 @@
+%D \module
+%D [ file=m-chart,
+%D version=2011.10.1, % 1998.10.10,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Flow Charts,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% todo (if used):
+% flowsets
+% autoscaling (bodyfontsteps)
+% comment
+% overlay
+%
+% todo:
+% \useFLOWchart[name][parent][setting,setting][additional settings]
+% \useFLOWchart[name][parent][additional settings]
+% setups
+% mp instance with less files
+
+\registerctxluafile{m-chart}{}
+
+\unprotect
+
+% todo: figure out a nice way to define the lot: share current and
+% support current as name (nb: we need to set parent then)
+
+\installcorenamespace {flowchart} % \def\??flch{@@flch} % chart
+\installcorenamespace {flowline} % \def\??flln{@@flln} % line
+\installcorenamespace {flowshape} % \def\??flsh{@@flsh} % shape
+\installcorenamespace {flowfocus} % \def\??flfc{@@flfc} % focus
+\installcorenamespace {flowsets} % \def\??flst{@@flst} % sets
+\installcorenamespace {flowsplit} % \def\??flsp{@@flsp} % split
+
+\installsimplecommandhandler \??flowchart {FLOWchart} \??flowchart % maybe just a setuphandler
+\installsimplecommandhandler \??flowline {FLOWline} \??flowline % maybe just a setuphandler
+\installsimplecommandhandler \??flowshape {FLOWshape} \??flowshape % maybe just a setuphandler
+\installsimplecommandhandler \??flowfocus {FLOWfocus} \??flowfocus % maybe just a setuphandler
+\installsimplecommandhandler \??flowsets {FLOWsets} \??flowsets % maybe just a setuphandler
+\installsimplecommandhandler \??flowsplit {FLOWsplit} \??flowsplit % maybe just a setuphandler
+
+\let\setupFLOWcharts\setupFLOWchart
+\let\setupFLOWlines \setupFLOWline
+\let\setupFLOWshapes\setupFLOWshape
+\let\setupFLOWsets \setupFLOWset
+
+\setupFLOWcharts
+ [\c!width=12\bodyfontsize,
+ \c!height=7\bodyfontsize,
+ \c!offset=\FLOWlineparameter\c!rulethickness,
+ \c!dx=2\bodyfontsize,
+ \c!dy=2\bodyfontsize,
+ \c!nx=0,
+ \c!ny=0,
+ \c!x=1,
+ \c!y=1,
+ \c!labeloffset=.5\bodyfontsize,
+ \c!commentoffset=.5\bodyfontsize,
+ \c!exitoffset=\zeropoint,
+ % \c!split=\v!no,
+ % \c!maxwidth=,
+ % \c!maxheight=,
+ % \c!option=,
+ % \c!bodyfont=,
+ % \c!dot=, % private option
+ % \c!autofocus=,
+ % \c!focus=,
+ % \c!background=,
+ % \c!framecolor=
+ % \c!backgroundcolor=, % \s!white
+ \c!rulethickness=.15\bodyfontsize, %\linewidth,
+ \c!frame=\v!off]
+
+\setupFLOWlines
+ [\c!corner=\v!round,
+ \c!arrow=\v!yes,
+ \c!dash=\v!no,
+ \c!radius=.375\bodyfontsize,
+ \c!color=FLOWlinecolor,
+ \c!rulethickness=.15\bodyfontsize,
+ \c!offset=\zeropoint]
+
+\setupFLOWshapes
+ [\c!default=action,
+ \c!framecolor=FLOWframecolor,
+ \c!background=\v!color,
+ \c!backgroundcolor=FLOWbackgroundcolor,
+ \c!rulethickness=.15\bodyfontsize,
+ \c!offset=.5\bodyfontsize]
+
+\setupFLOWfocus
+ [\c!framecolor=FLOWfocuscolor,
+ \c!background=\FLOWshapeparameter\c!background,
+ \c!backgroundcolor=\FLOWshapeparameter\c!backgroundcolor,
+ \c!rulethickness=\FLOWshapeparameter\c!rulethickness,
+ \c!offset=\FLOWshapeparameter\c!offset]
+
+\setupFLOWsplit
+ [\c!dx=0,
+ \c!dy=0,
+ % \c!command=,
+ % \c!before=,
+ % \c!after=,
+ \c!nx=3,
+ \c!ny=4]
+
+\unexpanded\def\beforeFLOWsplit{\FLOWsplitparameter\c!before}
+\unexpanded\def\afterFLOWsplit {\FLOWsplitparameter\c!after}
+\unexpanded\def\handleFLOWsplit{\FLOWsplitparameter\c!command}
+
+\definecolor [FLOWfocuscolor] [s=.2]
+\definecolor [FLOWlinecolor] [s=.5]
+\definecolor [FLOWframecolor] [s=.7]
+\definecolor [FLOWbackgroundcolor] [s=.9]
+
+\newtoks\everyFLOWchart
+
+\unexpanded\def\module_charts_process[#name]%
+ {\ctxcommand{flow_start_chart("#name")}}
+
+\unexpanded\def\startFLOWchart
+ {\startnointerference
+ \the\everyFLOWchart
+ \dosingleempty\module_charts_start_chart}
+
+\unexpanded\def\module_charts_start_chart[#name]%
+ {\ctxcommand{flow_start_chart("#name")}}
+
+\unexpanded\def\stopFLOWchart
+ {\ctxcommand{flow_stop_chart()}%
+ \stopnointerference}
+
+\unexpanded\def\defineFLOWchart % for old times sake
+ {\dodoubleempty\module_charts_FLOW_define}
+
+\unexpanded\def\module_charts_FLOW_define[#name][#settings]#cells% todo: save settings
+ {\startnointerference
+ \the\everyFLOWchart
+ \ctxcommand{flow_start_chart("#name")}%
+ #cells%
+ \ctxcommand{flow_stop_chart()}%
+ \stopnointerference}
+
+\unexpanded\def\startFLOWcell
+ {\dodoubleempty\module_charts_start_cell}
+
+\unexpanded\def\module_charts_start_cell[#1][#2]%
+ {\begingroup
+ \iffirstargument
+ \setupFLOWshape[#1]%
+ \fi
+ \ifsecondargument
+ \setupFLOWline[#2]%
+ \fi
+ \ctxcommand{flow_start_cell {
+ shape = {
+ rulethickness = \number\dimexpr\FLOWshapeparameter\c!rulethickness,
+ default = "\FLOWshapeparameter\c!default",
+ framecolor = "\FLOWshapeparameter\c!framecolor",
+ backgroundcolor = "\FLOWshapeparameter\c!backgroundcolor",
+ },
+ focus = {
+ rulethickness = \number\dimexpr\FLOWfocusparameter\c!rulethickness,
+ framecolor = "\FLOWfocusparameter\c!framecolor",
+ backgroundcolor = "\FLOWfocusparameter\c!backgroundcolor",
+ },
+ line = {
+ rulethickness = \number\dimexpr\FLOWlineparameter\c!rulethickness,
+ radius = \number\dimexpr\FLOWlineparameter\c!radius,
+ color = "\FLOWlineparameter\c!color",
+ corner = "\FLOWlineparameter\c!corner",
+ dash = "\FLOWlineparameter\c!dash",
+ arrow = "\FLOWlineparameter\c!arrow",
+ offset = \number\dimexpr\FLOWlineparameter\c!offset,
+ },
+ } }%
+ \endgroup}
+
+\unexpanded\def\stopFLOWcell
+ {\ctxcommand{flow_stop_cell()}}
+
+\unexpanded\def\FLOWchart
+ {\dodoubleempty\module_charts_process}
+
+\def\module_charts_process[#name][#settings]%
+ {\bgroup % \vbox removed
+ \insidefloattrue
+ \dontcomplain
+ \setupFLOWchart[#settings]%
+ \usebodyfontparameter\FLOWchartparameter
+ \ctxcommand{flow_make_chart {
+ chart = {
+ name = "#name",
+ option = "\FLOWchartparameter\c!option",
+ backgroundcolor = "\FLOWchartparameter\c!backgroundcolor",
+ width = \number\dimexpr\FLOWchartparameter\c!width,
+ height = \number\dimexpr\FLOWchartparameter\c!height,
+ dx = \number\dimexpr\FLOWchartparameter\c!dx,
+ dy = \number\dimexpr\FLOWchartparameter\c!dy,
+ offset = \number\dimexpr\FLOWchartparameter\c!offset,
+ % bodyfont = "\FLOWchartparameter\c!bodyfont",
+ dot = "\FLOWchartparameter\c!dot", % private option
+ hcompact = "\FLOWchartparameter\c!hcompact", % undocumented option
+ vcompact = "\FLOWchartparameter\c!vcompact", % undocumented option
+ focus = "\FLOWchartparameter\c!focus",
+ autofocus = "\FLOWchartparameter\c!autofocus",
+ nx = "\FLOWchartparameter\c!nx",
+ ny = "\FLOWchartparameter\c!ny",
+ x = "\FLOWchartparameter\c!x",
+ y = "\FLOWchartparameter\c!y",
+ labeloffset = \number\dimexpr\FLOWchartparameter\c!labeloffset,
+ commentoffset = \number\dimexpr\FLOWchartparameter\c!commentoffset,
+ exitoffset = \number\dimexpr\FLOWchartparameter\c!exitoffset,
+ split = "\FLOWchartparameter\c!split",
+ },
+ shape = {
+ rulethickness = \number\dimexpr\FLOWshapeparameter\c!rulethickness,
+ default = "\FLOWshapeparameter\c!default",
+ framecolor = "\FLOWshapeparameter\c!framecolor",
+ backgroundcolor = "\FLOWshapeparameter\c!backgroundcolor",
+ },
+ focus = {
+ rulethickness = \number\dimexpr\FLOWfocusparameter\c!rulethickness,
+ framecolor = "\FLOWfocusparameter\c!framecolor",
+ backgroundcolor = "\FLOWfocusparameter\c!backgroundcolor",
+ },
+ line = {
+ rulethickness = \number\dimexpr\FLOWlineparameter\c!rulethickness,
+ radius = \number\dimexpr\FLOWlineparameter\c!radius,
+ color = "\FLOWlineparameter\c!color",
+ corner = "\FLOWlineparameter\c!corner",
+ dash = "\FLOWlineparameter\c!dash",
+ arrow = "\FLOWlineparameter\c!arrow",
+ offset = "\FLOWlineparameter\c!offset",
+ },
+ set = {
+ },
+ split = {
+ nx = \number\FLOWsplitparameter\c!nx,
+ ny = \number\FLOWsplitparameter\c!ny,
+ dx = \number\FLOWsplitparameter\c!dx,
+ dy = \number\FLOWsplitparameter\c!dy,
+ command = "",
+ marking = "\FLOWsplitparameter\c!marking",
+ before = "",
+ after = "",
+ }
+ } }%
+ \egroup}
+
+\unexpanded\def\FLOWcharts
+ {\dodoubleempty\FLOW_charts}
+
+\def\FLOW_charts[#name][#settings]
+ {\begingroup
+ \setupFLOWchart[\c!split=\v!yes]%
+ \setupFLOWsplit[#settings]%
+ \module_charts_process[#name][]% \FLOWchart...
+ \endgroup}
+
+\appendtoks
+ \let\name \FLOW_name
+ \let\shape \FLOW_shape
+ \let\destination\FLOW_destination
+ \let\focus \FLOW_focus
+ \let\overlay \FLOW_overlay
+ \let\location \FLOW_location
+ \let\text \FLOW_text
+ \let\label \FLOW_label
+ \let\comment \FLOW_comment
+ \let\exit \FLOW_exit
+ \let\connection \FLOW_connection
+ \let\include \FLOW_include
+ \let\figure \FLOW_figure
+ %
+ \let\connect \FLOW_connection
+ \let\locate \FLOW_location
+ %
+ \let\includeFLOWchart\include
+\to \everyFLOWchart
+
+\unexpanded\def\FLOW_name #name{\ctxcommand{flow_set_name("#name")}\ignorespaces}
+\unexpanded\def\FLOW_shape #shape{\ctxcommand{flow_set_shape("#shape")}\ignorespaces}
+\unexpanded\def\FLOW_destination#destination{\ctxcommand{flow_set_destination("#destination")}\ignorespaces}
+\unexpanded\def\FLOW_focus #focus{\ctxcommand{flow_set_focus("#focus")}\ignorespaces}
+\unexpanded\def\FLOW_overlay #overlay{\ctxcommand{flow_set_overlay("#overlay")}\ignorespaces}
+\unexpanded\def\FLOW_location #location{\ctxcommand{flow_set_location("#location")}\ignorespaces}
+\unexpanded\def\FLOW_figure #figure{\ctxcommand{flow_set_figure("#figure")}\ignorespaces}
+
+\unexpanded\def\FLOW_text {\dosingleempty\module_charts_FLOW_text}
+\unexpanded\def\FLOW_label {\dosingleempty\module_charts_FLOW_label}
+\unexpanded\def\FLOW_comment {\dosingleempty\module_charts_FLOW_comment}
+\unexpanded\def\FLOW_exit {\dosingleempty\module_charts_FLOW_exit}
+\unexpanded\def\FLOW_connection{\dodoubleempty\module_charts_FLOW_connection}
+\unexpanded\def\FLOW_include {\dodoubleempty\module_charts_FLOW_include}
+
+\unexpanded\def\module_charts_FLOW_text [#align]#text{\ctxcommand{flow_set_text("#align",\!!bs\detokenize{#text}\!!es)}\ignorespaces}
+\unexpanded\def\module_charts_FLOW_label [#location]#text{\ctxcommand{flow_set_label("#location",\!!bs\detokenize{#text}\!!es)}\ignorespaces}
+\unexpanded\def\module_charts_FLOW_comment [#location]#text{\ctxcommand{flow_set_comment("#location",\!!bs\detokenize{#text}\!!es)}\ignorespaces}
+\unexpanded\def\module_charts_FLOW_exit [#location]#text{\ctxcommand{flow_set_exit("#location",\!!bs\detokenize{#text}\!!es)}\ignorespaces}
+\unexpanded\def\module_charts_FLOW_connection[#location][#offset]#name{\ctxcommand{flow_set_connection("#location","#offset","#name")}\ignorespaces}
+
+\unexpanded\def\module_charts_FLOW_include [#name][#settings]{%
+ \begingroup
+ \getparameters[FLOWi][x=1,y=1,#settings]%
+ \ctxcommand{flow_set_include("#name",\number\FLOWix,\number\FLOWiy,\!!bs\detokenize{#settings}\!!es)}%
+ \endgroup
+ \ignorespaces
+}
+
+\setvariables
+ [flowcell:text]
+ [x=1,
+ y=1,
+ text=,
+ align=,
+ set=\setups{flowcell:text:place}]
+
+\def\FLOWx{\getvariable{flowcell:text}{x}} % compatibility (for Willi)
+\def\FLOWy{\getvariable{flowcell:text}{y}} % compatibility (for Willi)
+
+% \c!background={\@@FLOWbackground,\FLOWoverlay},
+
+\defineoverlay
+ [flowcell:figure]
+ [\overlayfigure{\getvariable{flowcell:text}{figure}}]
+
+\startsetups flowcell:text:place
+ \begingroup
+ \iftrialtypesetting
+ \directsetup{flowcell:text:place:indeed}
+ \else \iflocation
+ \doifelsenothing {\getvariable{flowcell:text}{destination}} {
+ \directsetup{flowcell:text:place:indeed}
+ } {
+ % tricky: scaling and moving around is not taken into account
+ \setupinteraction[\c!color=,\c!contrastcolor=]
+ \gotobox{\directsetup{flowcell:text:place:indeed}}[\getvariable{flowcell:text}{destination}]
+ }
+ \else
+ \directsetup{flowcell:text:place:indeed}
+ \fi \fi
+ \endgroup
+\stopsetups
+
+\startsetups flowcell:text:place:indeed
+ \begingroup
+ \directsetup{flowcell:text:user}
+ \doifelsenothing {\getvariable{flowcell:text}{figure}} {
+ \expandcheckedcsname{flowcell:}{\getvariable{flowcell:text}{align}}\empty
+ {\getvariable{flowcell:text}{text}}
+ } {
+ \expandcheckedcsname{flowcell:}{\getvariable{flowcell:text}{align}}\empty
+ [\c!background=flowcell:figure]
+ {\getvariable{flowcell:text}{text}}
+ }
+ \endgroup
+\stopsetups
+
+\defineframed % to be discussed: shape or global
+ [flowcell:base]
+ [\c!offset=\v!overlay, % no strut ?
+ \c!frame=\FLOWchartparameter\c!frame,
+ \c!background=\FLOWchartparameter\c!background,
+ \c!backgroundcolor=\FLOWchartparameter\c!backgroundcolor,
+ %\c!foregroundcolor=\FLOWshapeparameter\c!foregroundcolor,
+ \c!align=\v!middle,
+ \c!bottom=\vfill,
+ \c!top=\vfill,
+ \c!width=\FLOWchartparameter\c!width,
+ \c!height=\FLOWchartparameter\c!height,
+ % \c!rulethickness=\FLOWchartparameter\c!rulethickness,
+ \c!rulethickness=\zeropoint, % comment for tracing
+ \c!framecolor=\FLOWchartparameter\c!framecolor]
+
+\defineframed[flowcell:] [flowcell:base]
+\defineframed[flowcell:l] [flowcell:base][\c!align=\v!flushleft]
+\defineframed[flowcell:r] [flowcell:base][\c!align=\v!flushright]
+\defineframed[flowcell:m] [flowcell:base][\c!align=\v!middle]
+\defineframed[flowcell:c] [flowcell:base][\c!align=\v!middle]
+
+\defineframed[flowcell:t] [flowcell:base][\c!top=]
+\defineframed[flowcell:b] [flowcell:base][\c!bottom=]
+
+\defineframed[flowcell:lt][flowcell:base][\c!top=,\c!align=\v!flushleft]
+\defineframed[flowcell:rt][flowcell:base][\c!top=,\c!align=\v!flushright]
+\defineframed[flowcell:mt][flowcell:base][\c!top=,\c!align=\v!middle]
+\defineframed[flowcell:ct][flowcell:base][\c!top=,\c!align=\v!middle]
+
+\defineframed[flowcell:lb][flowcell:base][\c!bottom=,\c!align=\v!flushleft]
+\defineframed[flowcell:rb][flowcell:base][\c!bottom=,\c!align=\v!flushright]
+\defineframed[flowcell:mb][flowcell:base][\c!bottom=,\c!align=\v!middle]
+\defineframed[flowcell:cb][flowcell:base][\c!bottom=,\c!align=\v!middle]
+
+% \startsetups flowcell:text:user
+% \setupframed
+% [flowcell:base]
+% [background=flowcell]
+% \definelayer
+% [flowcell]
+% [width=\namedframedparameter{flowcell:base}{width},
+% height=\namedframedparameter{flowcell:base}{height}]
+% \setlayerframed
+% [flowcell]
+% [preset=rightbottom,offset=-2.75ex]
+% [frame=off]
+% {\tx\FLOWx.\FLOWy}
+% \stopsetups
+%
+% % or:
+%
+% \setupframed
+% [flowcell:base]
+% [background={flowcell-1,flowcell-2}]
+%
+% \defineoverlay
+% [flowcell-1]
+% [\directsetup{flowcell-1}]
+%
+% \definelayer
+% [flowcell-2]
+% [width=\overlaywidth,
+% height=\overlayheight]
+%
+% \startsetups flowcell-1
+% \setlayerframed
+% [flowcell-2]
+% [preset=rightbottom,offset=-2.75ex]
+% [frame=off]
+% {\tx\FLOWx.\FLOWy}
+% \stopsetups
+
+% %D \starttyping
+% %D \setupFLOWsplit
+% %D [nx=5,ny=10,
+% %D dx=0,dy=0,
+% %D before=,
+% %D after=\page]
+% %D
+% %D \FLOWcharts[mybigflow]
+% %D \stoptyping
+% %D
+% %D \starttyping
+% %D \splitfloat
+% %D {\placefigure{What a big flowchart this is!}}
+% %D {\FLOWcharts[mybigflow]}
+% %D \stoptyping
+
+% \setupFLOWsplit
+% [nx=5,
+% ny=8,
+% dx=1,
+% dy=1,
+% command=\framed,
+% before=\page,
+% after=\page]
+%
+% \FLOWchart[demo] \page
+% \FLOWchart[demo][split=yes] \page
+% \FLOWchart[demo][x=1,y=1,nx=5,ny=8] \page
+% \FLOWchart[demo][x=1,y=9,nx=5,ny=10] \page
+
+\protect
+
+\continueifinputfile{m-chart.mkvi}
+
+\input chrt-xml.tex
+
+\usemodule[abr-01]
+
+\setupFLOWcharts[option=test]
+\setupFLOWcharts[frame=on]
+\setupinteraction[state=start]
+
+% \setupFLOWcharts[dx=30pt,dy=30pt]
+
+\startMPinclusions
+ predefined_shapes[101] := fullcircle ;
+\stopMPinclusions
+
+\startFLOWchart[demo]
+ \startFLOWcell[framecolor=darkgray]
+ \name {start}
+ \location {1,1}
+ \shape {action}
+ \text {start}
+ \connection [bl] {one}
+ \stopFLOWcell
+ \startFLOWcell[framecolor=darkred]
+ \name {one}
+% \destination{CloseDocument}
+ \location {2,2}
+% \shape {action}
+ \shape {101}
+ \text {first}
+% \label [b] {\bfx bottom}
+ \connection [rt] {two}
+% \exit [l] {exit l}
+% \exit [r] {exit r}
+% \exit [t] {exit t}
+% \exit [b] {exit b}
+ \stopFLOWcell
+ \setupFLOWshapes[framecolor=darkgray]
+ \startFLOWcell % [foregroundcolor=white]
+ \name {two}
+% \destination{CloseDocument}
+ \location {3,3}
+ \shape {action}
+ \text {second}
+ \figure {cow.pdf}
+% \label [l] {\bfx left}
+% \exit [l] {exit l}
+% \exit [r] {exit r}
+% \exit [t] {exit t}
+% \exit [b] {exit b}
+ \stopFLOWcell
+\stopFLOWchart
+
+\starttext
+
+\startTEXpage
+% \FLOWchart[convert-en]
+% \FLOWchart[conversion 1]
+% \FLOWchart[conversion 7]
+% \FLOWchart[conversion 9]
+% \FLOWchart[conversion 10]
+ \FLOWchart[demo]
+
+\stopTEXpage
+
+\startTEXpage
+ \FLOWchart[conversion 10]
+\stopTEXpage
+
+\stoptext
diff --git a/tex/context/modules/mkiv/m-chemic.mkiv b/tex/context/modules/mkiv/m-chemic.mkiv
new file mode 100644
index 000000000..1dd403fae
--- /dev/null
+++ b/tex/context/modules/mkiv/m-chemic.mkiv
@@ -0,0 +1,20 @@
+%D \module
+%D [ file=ppchtex (m-chemic),
+%D version=1997.03.19,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=\PPCHTEX\ (Plain Pictex Context cHemie \TEX),
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D suggestions={Tobias Burnus, Dirk Kuypers \& Ton Otten},
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{ppchtex}{not loaded as support for chemistry in now built in}
+
+% \usemodule[pictex] % we will get rid of this
+% \input ppchtex.mkiv \relax
+
+\endinput
diff --git a/tex/context/modules/mkiv/m-cweb.mkiv b/tex/context/modules/mkiv/m-cweb.mkiv
new file mode 100644
index 000000000..2546b2342
--- /dev/null
+++ b/tex/context/modules/mkiv/m-cweb.mkiv
@@ -0,0 +1,1373 @@
+%D \module
+%D [ file=m-cweb,
+%D version=1997.01.15,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=\CWEB\ Pretty Printing Macros,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This module has to be redone in the mkiv way.
+
+%D First some auxiliary stuff, to be moved to system module.
+
+\def\dodofindfirstcharacter#1%
+ {\ifx#1\relax
+ \let\next=\egroup
+ \else
+ \handlecase
+ {\expandafter\ifnum\expandafter\catcode\expandafter`#1=11
+ \def\next##1\relax{\egroup\def\firstcharacter{#1}}%
+ \fi}%
+ \fi
+ \next}
+
+\def\dofindfirstcharacter#1#2%
+ {\def\firstcharacter{}%
+ \bgroup
+ \defconvertedargument\ascii{#2}%
+ \let\next\dodofindfirstcharacter
+ \let\handlecase#1%
+ \expandafter\next\ascii\relax}
+
+\def\normalcase#1%
+ {#1}
+
+\def\findfirstcharacter%
+ {\dofindfirstcharacter\lowercase}
+
+\def\FindFirstCharacter%
+ {\dofindfirstcharacter\normalcase}
+
+\def\FINDFIRSTCHARACTER%
+ {\dofindfirstcharacter\uppercase}
+
+% nog doen:
+%
+% \deactivateCWEB in output routine
+% status info
+% gelinkte entries
+% parskip en parindent
+
+%D \gdef\CWEBquote#1.{{\em Quote :}\ #1.} % checks the .
+
+%D This module (re)implements the \CWEB\ macros as defined in
+%D the file \type{cwebmac.tex}.
+%D
+%D \CWEB\ uses short, often one character long, names for
+%D macros. This is no real problem because no one is supposed
+%D to read and understand the files generated by \CWEB. The
+%D standard macros are meant for \PLAIN\ \TEX\ users. In
+%D \CONTEXT\ and other macro packages however, there is a
+%D potential conflict with format specific or user defined
+%D commands. Furthermore, the \CWEB\ macros implement their own
+%D output routines. When integrating \CWEB\ documents in
+%D another environment, the \CWEB\ specific macros have to be
+%D made local. The first part of this module is dedicated to
+%D this feature.
+%D
+%D Instead of using \type{\def} and \type{\let} for defining
+%D macros, we use:
+%D
+%D \starttyping
+%D \defCEBmacro arguments {meaning}
+%D \letCEBmacro arguments {meaning}
+%D \stoptyping
+%D
+%D \CWEB files contain implicit calls to macros that generate
+%D the table of contents, the lists of sections and the index.
+%D Because we want to be much more flexible, we implemented our
+%D own alternatives, and therefore have to bypass the original
+%D ones. The next macro is used for defining these obsolete
+%D \CWEB\ macros. The dummies take care of arguments.
+%D
+%D \starttyping
+%D \defCEBdummy arguments {meaning}
+%D \stoptyping
+%D
+%D The list of \CWEB\ specific macro names is saved in a
+%D \TOKENLIST. This serves two purposes. First it enables us to
+%D activate the \CWEB\ macros, which are saved under a
+%D different name, second it can be used to temporary restore
+%D the meanings, for instance when the output routine builds
+%D the page.
+
+\newtoks\CWEBmacros
+
+%D Activating and deactivating is done by means of:
+%D
+%D \starttyping
+%D \activateCWEB
+%D \deactivateCWEB
+%D \stoptyping
+%D
+%D Which are implemented as:
+
+\def\activateCWEB%
+ {\let\doCWEB=\activateCWEBmacro
+ \the\CWEBmacros}
+
+\def\deactivateCWEB%
+ {\let\doCWEB=\deactivateCWEBmacro
+ \the\CWEBmacros}
+
+%D The three definition macros append the name of the macro to
+%D the list. The first two macros save the meaning, the last one
+%D assigns \type{{}} to the macro and gobbles original meaning.
+
+\long\def\defCWEBmacro#1%
+ {\appendtoks\doCWEB#1\to\CWEBmacros
+ \setvalue{newCWEB\string#1}}
+
+\long\def\letCWEBmacro#1%
+ {\appendtoks\doCWEB#1\to\CWEBmacros
+ \letvalue{newCWEB\string#1}}
+
+\long\def\defCWEBdummy#1#2#%
+ {\appendtoks\doCWEB#1\to\CWEBmacros
+ \setvalue{newCWEB\string#1}#2{}%
+ \gobbleoneargument}
+
+%D The macro \type{\defCWEBdummy} of course takes care of the
+%D argument. This leaves the two (de|)|activating macros:
+
+\def\CWEBmacro#1%
+ {\getvalue{newCWEB\string#1}}
+
+\def\activateCWEBmacro#1%
+ {\letvalue{oldCWEB\string#1}=#1%
+ \def#1{\CWEBmacro#1}}
+
+\def\deactivateCWEBmacro#1%
+ {\expandafter\let\expandafter#1\expandafter=\csname oldCWEB\string#1\endcsname}
+
+%D I did consider loading the \CWEB\ macros using temporary
+%D substitutes of \type{\def}, \type{\font}, \type{\newbox} etc.
+%D The main problem is that the file contains more than
+%D definitions and taking all kind of assignments into account
+%D too would not make things easier. So I decided to stick to
+%D the method as just described.
+
+%D Now we're ready for the real job. What follows is a partial
+%D adaption of the file \type{cwebmac.tex}, version 3.1, dated
+%D September 1994 and written by Levy and Knuth. When possible
+%D we kept the original meaning, but we've granted ourselves
+%D the freedom to reformat the macro's for readibility.
+%D
+%D We'll only present the macros we actually use. The source
+%D however contains the original implementation.
+
+% standard macros for CWEB listings (in addition to plain.tex)
+% Version 3.1 --- September 1994.
+%
+% \ifx\documentstyle\undefined\else\endinput\fi % LaTeX will use other macros
+%
+% \xdef\fmtversion{\fmtversion+CWEB3.1}
+
+%D \macros{.}{}
+%D
+%D \CWEBquote preserve a way to get the dot accent (all
+%D other accents will still work as usual).
+
+\letCWEBmacro\: = \.
+
+% \parskip = 0pt % no stretch between paragraphs
+% \parindent = 1em % for paragraphs and for the first line of C text
+
+% \font\ninerm = cmr9
+% \let\mc = \ninerm % medium caps
+% \font\eightrm = cmr8
+% \let\sc = \eightrm % small caps (NOT a caps-and-small-caps font)
+% \let\mainfont = \tenrm
+% \let\cmntfont = \tenrm
+% \font\tenss = cmss10
+% \let\cmntfont = \tenss % alternative comment font
+% \font\titlefont = cmr7 scaled \magstep4 % title on the contents page
+% \font\ttitlefont = cmtt10 scaled \magstep2 % typewriter type in title
+% \font\tentex = cmtex10 % TeX extended character set (used in strings)
+% \fontextraspace\tentex = 0pt % no double space after sentences
+
+%D \macros{mc,sc,cmntfont,eightrm}{}
+%D
+%D The naming of the fonts in in line with those in \PLAIN\
+%D \TEX. Although \CONTEXT\ implements its own scheme, there is
+%D still support for the \PLAIN\ ones. We keep the original
+%D names, but change their meaning. That way the macros obey
+%D switching to other sizes or styles.
+
+\defCWEBmacro\mc {\tx}
+\defCWEBmacro\sc {\txx}
+\defCWEBmacro\cmntfont {\ss}
+\defCWEBmacro\eightrm {\tx}
+
+%D \macros{tentex,sevenrm,sevensy,teni}{}
+%D
+%D The next one uses a temporary solution. The \type{cmtex10}
+%D font is not part of the default mechanism. We make use of
+%D the \CONTEXT\ variables \type{\textface}, \type{\scriptface}
+%D and \type{\scriptscriptface}, which hold the current
+%D sizes.
+
+\defCWEBmacro\tentex%
+ {\font\next=cmtex10 at \textface
+ \fontextraspace\next\zeropoint
+ \next}
+
+\defCWEBmacro\sevenrm {\getvalue{\scriptface rmtf}}
+\defCWEBmacro\sevensy {\getvalue{\scriptface mmsy}}
+\defCWEBmacro\teni {\getvalue{\textface mmmi}}
+
+%D \macros{CWEBpt}{}
+%D
+%D The original macros are based on a 10~point bodyfont size. We
+%D therefore have to specify dimension in points a bit
+%D different. Specifications like .6pt are changed to
+%D \type{.06} times \type{\bodyfontsize}.
+
+\defCWEBmacro\CWEBpt {\bodyfontsize} % still dutch
+
+%D \macros{CEE,UNIX,TEX,CPLUSPLUS}{}
+%D
+%D Next come some logo's. It does not make much sense to use
+%D the \CONTEXT\ logo mechanism here, so we simply say:
+
+\defCWEBmacro \CEE/{{\mc C\spacefactor1000}}
+\defCWEBmacro \UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}}
+\defCWEBmacro \TEX/{\TeX}
+\defCWEBmacro\CPLUSPLUS/{{\mc C\PP\spacefactor1000}}
+\defCWEBmacro \Cee{\CEE/} % for backward compatibility
+
+%D \macros{\ }{}
+%D
+%D Now we come to the real work: the short commands that make
+%D up the typography.
+%D
+%D \CWEBquote italic type for identifiers.
+
+\defCWEBmacro\\#1%
+ {\leavevmode\hbox{\it#1\/\kern.05em}}
+
+%D \macros{\string|}{}
+%D
+%D \CWEBquote one letter identifiers look better this way.
+
+\defCWEBmacro\|#1%
+ {\leavevmode\hbox{$#1$}}
+
+%D \macros{\string\&}{}
+%D
+%D \CWEBquote boldface type for reserved words.
+
+\defCWEBmacro\&#1%
+ {\leavevmode
+ \hbox
+ {\def\_%
+ {\kern.04em
+ \vbox{\hrule width.3em height .06\CWEBpt}% .6pt}%
+ \kern.08em}%
+ \bf#1\/\kern.05em}}
+
+%D \macros{.}{}
+%D
+%D Here we use the previously saved period. This macro
+%D takes care of special characters in strings.
+
+\defCWEBmacro\.#1%
+ {\leavevmode
+ \hbox
+ {\tentex % typewriter type for strings
+ \let\\=\BS % backslash in a string
+ \let\{=\LB % left brace in a string
+ \let\}=\RB % right brace in a string
+ \let\~=\TL % tilde in a string
+ \let\ =\SP % space in a string
+ \let\_=\UL % underline in a string
+ \let\&=\AM % ampersand in a string
+ \let\^=\CF % circumflex in a string
+ #1\kern.05em}}
+
+%D \macros{)}{}
+%D
+%D Some discretionary hack.
+
+\defCWEBmacro\)%
+ {\discretionary{\hbox{\tentex\BS}}{}{}}
+
+%D \macros{AT}{}
+%D
+%D \CWEBquote at sign for control text (not needed in versions
+%D $>=$ 2.9).
+
+\defCWEBmacro\AT{@}
+
+%D \macros{ATL,postATL,NOATL}{}
+%D
+%D A two step macro that handles whatever.
+
+\defCWEBmacro\ATL%
+ {\par
+ \noindent
+ \bgroup
+ \catcode`\_=12
+ \postATL}
+
+\defCWEBmacro\postATL#1 #2 %
+ {\bf letter \\{\uppercase{\char"#1}} tangles as \tentex "#2"%
+ \egroup
+ \par}
+
+\defCWEBmacro\noATL#1 #2 %
+ {}
+
+%D \macros{noatl}{}
+%D
+%D \CWEBquote suppress output from \type{@l}.
+
+\defCWEBmacro\noatl%
+ {\let\ATL=\noATL}
+
+% \defCWEBmacro\ATH%
+% {\X\kern-.5em:Preprocessor definitions\X}
+
+%D \macros{PB}
+%D
+%D \CWEBquote hook for program brackets {\tttf\string|...\string|}
+%D in TeX part or section name.
+
+\defCWEBmacro\PB%
+ {\relax}
+
+% \chardef\AM = `\& % ampersand character in a string
+% \chardef\BS = `\\ % backslash in a string
+% \chardef\LB = `\{ % left brace in a string
+% \chardef\RB = `\} % right brace in a string
+% \chardef\TL = `\~ % tilde in a string
+% \chardef\UL = `\_ % underline character in a string
+% \chardef\CF = `\^ % circumflex character in a string
+
+\defCWEBmacro\AM {\char`\&} % ampersand character in a string
+\defCWEBmacro\BS {\char`\\} % backslash in a string
+\defCWEBmacro\LB {\char`\{} % left brace in a string
+\defCWEBmacro\RB {\char`\}} % right brace in a string
+\defCWEBmacro\TL {\char`\~} % tilde in a string
+\defCWEBmacro\UL {\char`\_} % underline character in a string
+\defCWEBmacro\CF {\char`\^} % circumflex character in a string
+
+\defCWEBmacro\SP {{\tt\char`\ }} % (visible) space in a string
+
+% \newbox\PPbox \setbox\PPbox=\hbox
+% {\kern.5pt\raise1pt\hbox{\sevenrm+\kern-1pt+}\kern.5pt}
+% \newbox\MMbox \setbox\MMbox=\hbox
+% {\kern.5pt\raise1pt\hbox{\sevensy\char0\kern-1pt\char0}\kern.5pt}
+% \newbox\MGbox \setbox\MGbox=\hbox % symbol for ->
+% {\kern-2pt\lower3pt\hbox{\teni\char'176}\kern1pt}
+% \newbox\MODbox \setbox\MODbox=\hbox
+% {\eightrm\%}
+%
+% \def\PP {\copy\PPbox}
+% \def\MM {\copy\MMbox}
+% \def\MG {\copy\MGbox}
+% \def\MOD {\mathbin{\copy\MODbox}}
+
+\defCWEBmacro\PP% symbol for ++
+ {\kern.05\CWEBpt
+ \raise.1\CWEBpt\hbox{\sevenrm+\kern-.1\CWEBpt+}%
+ \kern.05\CWEBpt}
+
+\defCWEBmacro\MM%
+ {\kern.05\CWEBpt
+ \raise.1\CWEBpt\hbox{\sevensy\char0\kern-.1\CWEBpt\char0}%
+ \kern.05\CWEBpt}
+
+\defCWEBmacro\MG%
+ {\kern-.2\CWEBpt
+ \lower.3\CWEBpt\hbox{\teni\char'176}%
+ \kern .1\CWEBpt}
+
+\defCWEBmacro\MRL#1%
+ {\mathrel{\let\K==#1}}
+
+% \def\MRL#1%
+% {\KK#1}
+% \def\KK#1#2%
+% {\buildrel\;#1\over{#2}}
+
+\letCWEBmacro\GG = \gg
+\letCWEBmacro\LL = \ll
+\letCWEBmacro\NULL = \Lambda
+
+% \mathchardef\AND = "2026 % bitwise and; also \& (unary operator)
+
+\defCWEBmacro\AND% redefines itself (funny)
+ {\mathchardef\AND="2026 \AND} % bitwise and; also \& (unary operator)
+
+\letCWEBmacro\OR = \mid % bitwise or
+\letCWEBmacro\XOR = \oplus % bitwise exclusive or
+\defCWEBmacro\CM {{\sim}} % bitwise complement
+\defCWEBmacro\MOD {\mathbin{\eightrm\%}}
+\defCWEBmacro\DC {\kern.1em{::}\kern.1em} % symbol for ::
+\defCWEBmacro\PA {\mathbin{.*}} % symbol for .*
+\defCWEBmacro\MGA {\mathbin{\MG*}} % symbol for ->*
+\defCWEBmacro\this {\&{this}}
+
+% \newbox \bak % backspace one em
+% \newbox \bakk % backspace two ems
+%
+% \setbox\bak =\hbox to -1em{}
+% \setbox\bakk=\hbox to -2em{}
+
+\newcount\CWEBind % current indentation in ems
+
+\defCWEBmacro\1% indent one more notch
+ {\global\advance\CWEBind by 1
+ \hangindent\CWEBind em}
+
+\defCWEBmacro\2% indent one less notch
+ {\global\advance\CWEBind by -1 }
+
+\defCWEBmacro\3#1% optional break within a statement
+ {\hfil
+ \penalty#10
+ \hfilneg}
+
+\defCWEBmacro\4% backspace one notch
+ {\hbox to -1em{}}
+
+\defCWEBmacro\5% optional break
+ {\hfil
+ \penalty-1
+ \hfilneg
+ \kern2.5em
+ \hbox to -2em{}%
+ \ignorespaces}
+
+\defCWEBmacro\6% forced break
+ {\ifmmode
+ \else
+ \par
+ \hangindent\CWEBind em
+ \noindent
+ \kern\CWEBind em
+ \hbox to -2em{}%
+ \ignorespaces
+ \fi}
+
+\defCWEBmacro\7% forced break and a little extra space
+ {\Y
+ \6}
+
+\defCWEBmacro\8% no indentation
+ {\hskip-\CWEBind em
+ \hskip 2em}
+
+\defCWEBmacro\9#1%
+ {}
+
+\newcount\gdepth % depth of current major group, plus one
+\newcount\secpagedepth
+\secpagedepth=3 % page breaks will occur for depths -1, 0, and 1
+
+% \newtoks\gtitle % title of current major group
+% \newskip\intersecskip
+% \intersecskip=12pt minus 3pt % space between sections
+
+% \let\yskip=\smallskip
+
+\defCWEBmacro\?%
+ {\mathrel?}
+
+% \def\note#1#2.%
+% {\Y\noindent
+% {\hangindent2em\baselineskip10pt\eightrm#1~#2.\par}}
+
+\defCWEBmacro\lapstar%
+ {\rlap{*}}
+
+% \def\stsec%
+% {\rightskip=0pt % get out of C mode (cf. \B)
+% \sfcode`;=1500
+% \pretolerance 200
+% \hyphenpenalty 50
+% \exhyphenpenalty 50
+% \noindent{\let\*=\lapstar\bf\secstar.\quad}}
+%
+% \let\startsection=\stsec
+
+\defCWEBmacro\defin#1%
+ {\global\advance\CWEBind by 2 \1\&{#1 } } % begin `define' or `format'
+
+% \def\A% xref for doubly defined section name
+% {\note{See also section}}
+%
+% \def\As% xref for multiply defined section name
+% {\note{See also sections}}
+
+\defCWEBmacro\B%
+ {\rightskip=0pt plus 100pt minus 10pt % go into C mode
+ \sfcode`;=3000
+ \pretolerance 10000
+ \hyphenpenalty 1000 % so strings can be broken (discretionary \ is inserted)
+ \exhyphenpenalty 10000
+ \global\CWEBind=2 \1\ \unskip}
+
+\defCWEBmacro\C#1%
+ {\5\5\quad$/\ast\,${\cmntfont #1}$\,\ast/$}
+
+% \let\SHC\C % "// short comments" treated like "/* ordinary comments */"
+
+\defCWEBmacro\SHC#1%
+ {\5\5\quad$//\,${\cmntfont#1}}
+
+% \def\C#1{\5\5\quad$\triangleright\,${\cmntfont#1}$\,\triangleleft$}
+% \def\SHC#1{\5\5\quad$\diamond\,${\cmntfont#1}}
+
+\defCWEBmacro\D% macro definition
+ {\defin{\#define}}
+
+\letCWEBmacro\E=\equiv % equivalence sign
+
+% \def\ET% conjunction between two section numbers
+% { and~}
+%
+% \def\ETs% conjunction between the last two of several section numbers
+% {, and~}
+
+\defCWEBmacro\F% format definition
+ {\defin{format}}
+
+\letCWEBmacro\G = \ge % greater than or equal sign
+
+% \H is long Hungarian umlaut accent
+
+\letCWEBmacro\I = \ne % unequal sign
+
+\defCWEBmacro\J% TANGLE's join operation
+ {\.{@\&}}
+
+% \let\K== % assignment operator
+
+\letCWEBmacro\K = \leftarrow % "honest" alternative to standard assignment operator
+
+% \L is Polish letter suppressed-L
+
+% \outer\def\M#1%
+% {\MN{#1}%
+% \ifon
+% \vfil
+% \penalty-100
+% \vfilneg % beginning of section
+% \vskip\intersecskip
+% \startsection
+% \ignorespaces}
+%
+% \outer\def\N#1#2#3.%
+% {\gdepth=#1%
+% \gtitle={#3}%
+% \MN{#2}% beginning of starred section
+% \ifon
+% \ifnum#1<\secpagedepth
+% \vfil
+% \eject % force page break if depth is small
+% \else
+% \vfil
+% \penalty-100
+% \vfilneg
+% \vskip\intersecskip
+% \fi
+% \fi
+% \message{*\secno}% progress report
+% \edef\next%
+% {\write\cont % write to contents file
+% {\ZZ{#3}{#1}{\secno}{\noexpand\the\pageno}}}%
+% \next % \ZZ{title}{depth}{sec}{page}
+% \ifon
+% \startsection
+% {\bf#3.\quad}%
+% \ignorespaces}
+%
+% \def\MN#1%
+% {\par % common code for \M, \N
+% {\xdef\secstar{#1}%
+% \let\*=\empty
+% \xdef\secno{#1}}% remove \* from section name
+% \ifx\secno\secstar
+% \onmaybe
+% \else
+% \ontrue
+% \fi
+% \mark{{{\tensy x}\secno}{\the\gdepth}{\the\gtitle}}}
+%
+% each \mark is {section reference or null}{depth plus 1}{group title}
+
+% \O is Scandinavian letter O-with-slash
+% \P is paragraph sign
+
+\defCWEBmacro\Q {\note{This code is cited in section}} % xref for mention of a section
+\defCWEBmacro\Qs {\note{This code is cited in sections}} % xref for mentions of a section
+
+% \S is section sign
+
+\defCWEBmacro\T#1%
+ {\leavevmode % octal, hex or decimal constant
+ \hbox
+ {$\def\?{\kern.2em}%
+ \def\$##1{\egroup_{\,\rm##1}\bgroup}% suffix to constant
+ \def\_{\cdot 10^{\aftergroup}}% power of ten (via dirty trick)
+ \let\~=\oct
+ \let\^=\hex
+ {#1}$}}
+
+\defCWEBmacro\U {\note{This code is used in section}} % xref for use of a section
+\defCWEBmacro\Us {\note{This code is used in sections}} % xref for uses of a section
+
+\letCWEBmacro\R = \lnot % logical not
+\letCWEBmacro\V = \lor % logical or
+\letCWEBmacro\W = \land % logical and
+
+% defined later on
+%
+% \def\X#1:#2\X%
+% {\ifmmode
+% \gdef\XX{\null$\null}%
+% \else
+% \gdef\XX{}%
+% \fi % section name
+% \XX$\langle\,${#2\eightrm\kern.5em#1}$\,\rangle$\XX}
+
+\unprotect
+
+\def\theCWEByskip {\blank[\v!small]}
+\def\theCWEBvskip {\blank[\v!big]}
+
+\protect
+
+\defCWEBmacro\Y%
+ {\par
+ \yskip}
+
+\defCWEBmacro\yskip%
+ {\theCWEByskip}
+
+\letCWEBmacro\Z = \le
+% \letCWEBmacro\ZZ = \let % now you can \write the control sequence \ZZ
+\letCWEBmacro\* = *
+
+\defCWEBmacro\oct%
+ {\hbox{$^\circ$\kern-.1em\it\aftergroup\?\aftergroup}}
+
+\defCWEBmacro\hex%
+ {\hbox{$^{\scriptscriptstyle\#}$\tt\aftergroup}}
+
+\defCWEBmacro\vb#1%
+ {\leavevmode
+ \hbox
+ {\kern.2\CWEBpt
+ \vrule
+ \vtop
+ {\vbox
+ {\hrule
+ \hbox{\strut\kern.2\CWEBpt\.{#1}\kern.2\CWEBpt}}
+ \hrule}%
+ \vrule
+ \kern.2\CWEBpt}} % verbatim string
+
+\def\onmaybe%
+ {\let\ifon=\maybe}
+
+\let\maybe=\iftrue
+
+\newif\ifon
+
+% \newif\iftitle
+% \newif\ifpagesaved
+%
+% \def\lheader%
+% {\mainfont
+% \the\pageno
+% \eightrm
+% \qquad
+% \grouptitle
+% \hfill
+% \title
+% \qquad
+% \mainfont
+% \topsecno} % top line on left-hand pages
+%
+% \def\rheader%
+% {\mainfont
+% \topsecno
+% \eightrm
+% \qquad
+% \title
+% \hfill
+% \grouptitle
+% \qquad
+% \mainfont
+% \the\pageno} % top line on right-hand pages
+%
+% \def\grouptitle
+% {\let\i=I
+% \let\j=J
+% \uppercase\expandafter{\expandafter\takethree\topmark}}
+%
+% \def\topsecno%
+% {\expandafter\takeone\topmark}
+%
+% \def\takeone #1#2#3{#1}
+% \def\taketwo #1#2#3{#2}
+% \def\takethree #1#2#3{#3}
+%
+% \def\nullsec%
+% {\eightrm
+% \kern-2em} % the \kern-2em cancels \qquad in headers
+%
+% \let\page=\pagebody % \def\page {\box255 }
+% \raggedbottom % \normalbottom % faster, but loses plain TeX footnotes
+%
+% \def\normaloutput#1#2#3%
+% {\shipout\vbox
+% {\ifodd
+% \pageno
+% \hoffset=\pageshift
+% \fi
+% \vbox to \fullpageheight
+% {\iftitle
+% \global\titlefalse
+% \else
+% \hbox to \pagewidth
+% {\vbox to 10pt{}%
+% \ifodd\pageno #3\else#2\fi}
+% \fi
+% \vfill#1}} % parameter #1 is the page itself
+% \global\advance\pageno by 1}
+%
+% \gtitle={\.{CWEB} output} % this running head is reset by starred sections
+%
+% \mark{\noexpand\nullsec0{\the\gtitle}}
+%
+% \def\title%
+% {\expandafter\uppercase\expandafter{\jobname}}
+%
+% \def\topofcontents%
+% {\centerline{\titlefont\title}
+% \vskip.7in
+% \vfill} % this material will start the table of contents page
+
+\def\botofcontents%
+ {\vfill
+ \centerline{\covernote}} % this material will end the table of contents page
+
+\def\covernote%
+ {}
+
+% some leftover
+
+\defCWEBmacro\contentspagenumber{0} % default page number for table of contents
+
+% \newdimen\pagewidth \pagewidth = 158mm % the width of each page
+% \newdimen\pageheight \pageheight = 223mm % the height of each page
+% \newdimen\fullpageheight \fullpageheight = 240mm % page height including headlines
+% \newdimen\pageshift \pageshift = 0in % shift righthand pages wrt lefthand ones
+%
+% \def\magnify#1%
+% {\mag=#1
+% \pagewidth=6.5truein
+% \pageheight=8.7truein
+% \fullpageheight=9truein
+% \setpage}
+%
+% \def\setpage%
+% {\hsize\pagewidth
+% \vsize\pageheight} % use after changing page size
+%
+% \def\contentsfile {\jobname.toc} % file that gets table of contents info
+% \def\readcontents {\input \contentsfile}
+% \def\readindex {\input \jobname.idx}
+% \def\readsections {\input \jobname.scn}
+%
+% \newwrite\cont
+% \output{\setbox0=\page % the first page is garbage
+% \openout\cont=\contentsfile
+% \write\cont{\catcode `\noexpand\@=11\relax} % \makeatletter
+% \global\output{\normaloutput\page\lheader\rheader}}
+% \setpage
+% \vbox to \vsize{} % the first \topmark won't be null
+
+\defCWEBdummy\magnify#1% magnify the page
+ {}
+
+\defCWEBmacro\ch%
+ {\note{The following sections were changed by the change file:}
+ \let\*=\relax}
+
+% \newbox\sbox % saved box preceding the index
+% \newbox\lbox % lefthand column in the index
+%
+% \def\inx%
+% {\par\vskip6pt plus 1fil % we are beginning the index
+% \def\page{\box255 }
+% \normalbottom
+% \write\cont{} % ensure that the contents file isn't empty
+% \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother
+% \closeout\cont % the contents information has been fully gathered
+% \output
+% {\ifpagesaved
+% \normaloutput{\box\sbox}\lheader\rheader
+% \fi
+% \global\setbox\sbox=\page
+% \global\pagesavedtrue}
+% \pagesavedfalse
+% \eject % eject the page-so-far and predecessors
+% \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box
+% \vsize=\pageheight
+% \advance\vsize by -\ht\sbox % the remaining height
+% \hsize=.5\pagewidth
+% \advance\hsize by -10pt
+% % column width for the index (20pt between cols)
+% \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines
+% \def\lr{L} % this tells whether the left or right column is next
+% \output
+% {\if L\lr
+% \global\setbox\lbox=\page
+% \gdef\lr{R}
+% \else
+% \normaloutput
+% {\vbox to\pageheight
+% {\box\sbox
+% \vss
+% \hbox to\pagewidth{\box\lbox\hfil\page}}}
+% \lheader
+% \rheader
+% \global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse\fi}
+% \message{Index:}
+% \parskip 0pt plus .5pt
+% \outer\def\I##1, {\par\hangindent2em\noindent##1:\kern1em} % index entry
+% \def\[##1]{$\underline{##1}$} % underlined index item
+% \rm
+% \rightskip0pt plus 2.5em
+% \tolerance 10000
+% \let\*=\lapstar
+% \hyphenpenalty 10000
+% \parindent0pt
+% \readindex}
+%
+% \def\fin%
+% {\par\vfill\eject % this is done when we are ending the index
+% \ifpagesaved\null\vfill\eject\fi % output a null index column
+% \if L\lr\else\null\vfill\eject\fi % finish the current page
+% \parfillskip 0pt plus 1fil
+% \def\grouptitle{NAMES OF THE SECTIONS}
+% \let\topsecno=\nullsec
+% \message{Section names:}
+% \output={\normaloutput\page\lheader\rheader}
+% \setpage
+% \def\note##1##2.{\quad{\eightrm##1~##2.}}
+% \def\Q{\note{Cited in section}} % crossref for mention of a section
+% \def\Qs{\note{Cited in sections}} % crossref for mentions of a section
+% \def\U{\note{Used in section}} % crossref for use of a section
+% \def\Us{\note{Used in sections}} % crossref for uses of a section
+% \def\I{\par\hangindent 2em}\let\*=*
+% \readsections}
+%
+% \def\con%
+% {\par\vfill\eject % finish the section names
+% %\ifodd\pageno\else\titletrue\null\vfill\eject\fi % for duplex printers
+% \rightskip = 0pt
+% \hyphenpenalty = 50
+% \tolerance = 200
+% \setpage
+% \output={\normaloutput\page\lheader\rheader}
+% \titletrue % prepare to output the table of contents
+% \pageno=\contentspagenumber
+% \def\grouptitle{TABLE OF CONTENTS}
+% \message{Table of contents:}
+% \topofcontents
+% \line{\hfil Section\hbox to3em{\hss Page}}
+% \let\ZZ=\contentsline
+% \readcontents\relax % read the contents info
+% \botofcontents
+% \end} % print the contents page(s) and terminate
+%
+% \def\contentsline#1#2#3#4%
+% {\ifnum#2=0
+% \smallbreak
+% \fi
+% \line{\consetup{#2}#1
+% \rm\leaders\hbox to .5em{.\hfil}\hfil\ #3\hbox to3em{\hss#4}}}
+%
+
+\defCWEBmacro\consetup#1%
+ {\ifcase#1 \bf % depth -1 (@**)
+ \or % depth 0 (@*)
+ \or \hskip2em % depth 1 (@*1)
+ \or \hskip4em % depth 2 (@*2)
+ \or \hskip6em % depth 3 (@*3)
+ \or \hskip8em % depth 4 (@*4)
+ \or \hskip10em % depth 5 (@*5)
+ \else \hskip12em
+ \fi} % depth 6 or more
+
+\defCWEBdummy \inx {} % index
+\defCWEBdummy \fin {} % finish
+\defCWEBdummy \con {} % table of contents and finish
+
+\defCWEBdummy \noinx {} % no indexes or table of contents
+\defCWEBdummy \nosecs {} % no index of section names or table of contents
+\defCWEBdummy \nocon {} % no table of contents
+
+\defCWEBmacro\,%
+ {\relax
+ \ifmmode
+ \mskip\thinmuskip
+ \else
+ \thinspace
+ \fi}
+
+% \def\noinx%
+% {\let\inx=\end}
+%
+% \def\nosecs%
+% {\let\FIN=\fin
+% \def\fin%
+% {\let\parfillskip=\end
+% \FIN}}
+%
+% \def\nocon%
+% {\let\con=\end}
+%
+% \newcount\twodigits
+%
+% \def\hours%
+% {\twodigits=\time
+% \divide\twodigits by 60
+% \printtwodigits
+% \multiply\twodigits by -60
+% \advance\twodigits by \time
+% :\printtwodigits}
+%
+% \def\gobbleone1{}
+%
+% \def\printtwodigits%
+% {\advance\twodigits by 100
+% \expandafter\gobbleone\number\twodigits
+% \advance\twodigits by -100 }
+%
+% \def\today%
+% {\ifcase\month
+% \or January\or February\or March\or April\or May\or June%
+% \or July\or August\or September\or October\or November\or December%
+% \fi
+% \space
+% \number\day, \number\year}
+%
+% \def\datethis%
+% {\def\startsection%
+% {\leftline{\sc\today\ at \hours}
+% \bigskip
+% \let\startsection=\stsec
+% \stsec}}
+%
+% \def\datecontentspage%
+% {\def\topofcontents%
+% {\leftline{\sc\today\ at \hours}
+% \bigskip
+% \centerline{\titlefont\title}
+% \vfill}}
+
+\defCWEBdummy\datethis {} % say `\datethis' in limbo, to get your listing timestamped before section 1
+\defCWEBdummy\datecontentspage {} % timestamps the contents page
+
+\defCWEBmacro\TeX%
+ {{\ifmmode\it\fi
+ \leavevmode
+ \hbox{T\kern-.1667em\lower.424ex\hbox{E}\hskip-.125em X}}}
+
+% alternative implementation
+
+\newif\ifCWEBnotes
+
+\defCWEBmacro\Q {\CWEBnotesfalse \note{This code is cited in section}} % xref for mention of a section
+\defCWEBmacro\Qs {\CWEBnotestrue \note{This code is cited in sections}} % xref for mentions of a section
+
+\defCWEBmacro\U {\CWEBnotesfalse \note{This code is used in section}} % xref for use of a section
+\defCWEBmacro\Us {\CWEBnotestrue \note{This code is used in sections}} % xref for uses of a section
+
+\defCWEBmacro\A {\CWEBnotesfalse \note{See also section}} % xref for doubly defined section name
+\defCWEBmacro\As {\CWEBnotestrue \note{See also sections}} % xref for multiply defined section name
+
+\defCWEBmacro\ET% conjunction between two section numbers
+ { and~}
+
+\defCWEBmacro\ETs% conjunction between the last two of several section numbers
+ {, and~}
+
+%\def\processCWEBsectionnumbers[#1]%
+% {\bgroup
+% \def\CWEBcomma%
+% {\def\CWEBcomma{, }}%
+% \def\docommand##1%
+% {\bgroup
+% \def\[####1]{####1}%
+% \xdef\CWEBreference{##1}%
+% \egroup
+% \CWEBcomma{\naar{\donottest{##1}}[web:\CWEBreference]}}%
+% \processcommalist[{#1}]\docommand
+% \egroup}
+
+% \def\processCWEBsectionnumbers[#1]%
+% {\bgroup
+% \def\CWEBcomma%
+% {\def\CWEBcomma{, }}%
+% \def\docommand##1%
+% {\bgroup
+% \def\(####1){####1}%
+% \xdef\CWEBreference{##1}%
+% \egroup
+% \CWEBcomma
+% {\localcolortrue\naar{\donottest{##1}}[web:\CWEBreference]}}%
+% \bgroup
+% \def\[##1]{\(##1)}\let\(=\relax\xdef\CWEBreferences{#1}%
+% \egroup
+% \unexpanded\def\(##1){\[##1]}%
+% \processcommacommand[\CWEBreferences]\docommand
+% \egroup}
+
+\def\processCWEBsectionnumbers[#1]%
+ {\bgroup
+ \def\CWEBcomma%
+ {\def\CWEBcomma{, }}%
+ \def\docommand##1%
+ {\bgroup
+ \def\[####1]{####1}%
+ \xdef\CWEBreference{##1}%
+ \egroup
+ \CWEBcomma{\localcolortrue\goto{\donottest{##1}}[web:\CWEBreference]}}%
+ \processlist{(}{)}{,}\docommand(#1)
+ \egroup}
+
+\def\processCWEBsectionnotes%
+ {\catcode`\s=12
+ \doprocessCWEBsectionnotes}
+
+\def\doprocessCWEBsectionnotes#1.%
+ {\ifCWEBnotes
+ \def\next##1\ET##2##3.%
+ {\processCWEBsectionnumbers[##1]%
+ \if##2s%
+ {, and~\goto{##3}[web:##3]}%
+ \else
+ { and~\goto{##2##3}[web:##2##3]}%
+ \fi}%
+ \next#1.%
+ \else
+ \goto{#1}[web:#1]%
+ \fi
+ \afterCWEBnote % inside group!
+ \egroup}
+
+\let\afterCWEBnote=\relax
+
+\defCWEBmacro\note#1%
+ {\bgroup
+ \Y\noindent
+ \def\afterCWEBnote{\par}%
+ \hangindent2em
+ %\baselineskip10pt
+ \eightrm#1~\processCWEBsectionnotes}
+
+\def\oldCWEBmacroX#1:#2\X% original
+ {\ifmmode
+ \gdef\XX{\null$\null}%
+ \else
+ \gdef\XX{}%
+ \fi % section name
+ \XX$\langle\,${#2\eightrm\kern.5em#1}$\,\rangle$\XX}
+
+\defCWEBmacro\ATH%
+ {\oldCWEBmacroX\kern-.5em:Preprocessor definitions\X}
+
+\def\newCWEBmacroX#1:#2\X% original
+ {\ifmmode
+ \gdef\XX{\null$\null}%
+ \else
+ \gdef\XX{}%
+ \fi % section name
+ \XX$\langle\,$%
+ {#2\eightrm\kern.5em\processCWEBsectionnumbers[{#1}]}%
+ $\,\rangle$\XX}
+
+\defCWEBmacro\X#1:#2\X%
+ {\newCWEBmacroX#1:#2\X}
+
+\definemarking[CWEBfilename]
+\definemarking[CWEBsectiontitle]
+\definemarking[CWEBsectionnumber]
+\definemarking[CWEBsectiondepth]
+
+\defCWEBmacro\M#1%
+ {\MN{#1}%
+ \ifon
+ \vfil
+ \penalty-100
+ \vfilneg % beginning of section
+ \theCWEBvskip
+ \startsection
+ \pagereference[web:#1]%
+ \expanded{\marking[CWEBsectionnumber]{\secno}}%
+ \expanded{\marking[CWEBsectiondepth]{\the\gdepth}}%
+ \ignorespaces}
+
+\defCWEBmacro\N#1#2#3.%
+ {\gdepth=#1%
+ \MN{#2}% beginning of starred section
+ \ifon
+ \ifnum#1<\secpagedepth
+ \vfil
+ \eject % force page break if depth is small
+ \else
+ \vfil
+ \penalty-100
+ \vfilneg
+ \theCWEBvskip
+ \fi
+ \fi
+ \message{*\secno}% progress report
+ \makesectionformat % context
+ \defconvertedargument\ascii{#3}%
+ \edef\next%
+ {\write\CWEBcont % write to contents file
+ {\string\ZZ{\ascii}{#1}{\secno}%
+ {\sectionformat::\noexpand\userfolio}{\noexpand\realfolio}}}%
+ \next % \ZZ{title}{depth}{sec}{page}
+ \ifon
+ \startsection
+ \pagereference[web:#2]%
+ \marking[CWEBsectiontitle] {#3}%
+ \expanded{\marking[CWEBsectionnumber]{\secno}}%
+ \expanded{\marking[CWEBsectiondepth]{\the\gdepth}}%
+ {\bf#3.\quad}%
+ \ignorespaces}
+
+\defCWEBmacro\MN#1%
+ {\par % common code for \M, \N
+ {\xdef\secstar{#1}%
+ \let\*=\empty
+ \xdef\secno{#1}}% remove \* from section name
+ \ifx\secno\secstar
+ \onmaybe
+ \else
+ \ontrue
+ \fi}
+
+\newif\iflinktoCWEBfile
+
+\def\setCWEBlinkfile#1%
+ {\linktoCWEBfiletrue
+ \def\otherCWEBfile{#1}}
+
+\unprotect
+
+\def\gotoCWEBsection#1[#2]%
+ {\iflinktoCWEBfile
+ \bgroup
+ \setupinteraction[\c!color=,\c!style=]%
+ \let\savedreferenceprefix=\referenceprefix
+ \localcolortrue
+ \goto{#1}[\otherCWEBfile::\savedreferenceprefix web:#2]%
+ \egroup
+ \else
+ #1%
+ \fi}
+
+\protect
+
+\defCWEBmacro\startsection%
+ {\rightskip=0pt % get out of C mode (cf. \B)
+ \sfcode`;=1500
+ \pretolerance 200
+ \hyphenpenalty 50
+ \exhyphenpenalty 50
+ \noindent
+ \bgroup
+ \let\*=\lapstar
+ \gotoCWEBsection{\bf\secstar.\quad}[\secno]%
+ \egroup}
+
+\def\ignoreCWEBinput%
+ {\let\normalinput=\input
+ \def\input ##1 %
+ {\let\input=\normalinput}}
+
+\def\loadCWEBmacros#1%
+ {\let\oldN=\N
+ \def\N{\bgroup\setbox0=\vbox\bgroup\endinput}%
+ \ignoreCWEBinput
+ \ReadFile{#1.tex}%
+ \egroup\egroup
+ \let\N=\oldN}
+
+\def\resetCWEBcontext%
+ {\catcode`\|=12 % used in context discretionaries
+ \everypar{} % used for context indentation and floats
+ \parskip=0pt % no stretch between cweb paragraphs
+ \parindent=1em} % is related to cweb backspace etc
+
+\newwrite\CWEBcont
+
+\def\processCWEBsource #1 %
+ {\bgroup
+ \resetCWEBcontext
+ \activateCWEB
+ \ignoreCWEBinput
+ \immediate\openout\CWEBcont=#1.toc
+ \write\CWEBcont{\noexpand\unprotect}
+ \message{Source:}
+ \marking[CWEBfilename]{#1}
+ \ReadFile{#1.tex}\relax
+ \write\CWEBcont{\noexpand\protect}
+ \closeout\CWEBcont
+ \par
+ \egroup}
+
+\def\resetCWEBindexentry%
+ {\xdef\currentCWEBindexentry{}}
+
+\def\showCWEBindexentry#1% can be redefined
+ {\theCWEBvskip
+ \vskip3\lineheight
+ \goodbreak
+ \vskip-3\lineheight
+ {\pagereference[web:#1]\bf#1}%
+ \theCWEBvskip}
+
+\def\checkCWEBindexentry#1%
+ {\bgroup
+ \def\\##1{##1}% a dummy that also removes the {}
+ \def\|##1{##1}% another dummy
+ \def\.##1{*##1}% and another (the typewriter one)
+ \def\&##1{##1}% and a last one
+ \def\9##1{##1}% hold this one
+ \catcode`*=11
+ \expandafter\def\expandafter\entry\expandafter{#1}%
+ \defconvertedcommand\ascii\entry
+ \expanded{\FINDFIRSTCHARACTER{\ascii}}%
+ \doifnot{\currentCWEBindexentry}{\firstcharacter}
+ {\doifnot{\firstcharacter}{*} % signal for \firstbunch
+ {\global\let\currentCWEBindexentry=\firstcharacter
+ \showCWEBindexentry{\currentCWEBindexentry}}}%
+ \egroup}
+
+\def\theCWEBbeforeindex {\startcolumns}
+\def\theCWEBafterindex {\stopcolumns}
+
+\def\processCWEBindex #1 %
+ {\bgroup
+ \resetCWEBcontext
+ \activateCWEB
+ \resetCWEBindexentry
+ \def\I##1, %
+ {\par
+ \checkCWEBindexentry{##1}%
+ \hangindent2em
+ \noindent##1:\kern1em%
+ \def\next####1.%
+ {\processCWEBsectionnumbers[{####1}]}%
+ \next}%
+ \def\[##1]%
+ {$\underline{##1}$}%
+ \let\*=\lapstar
+ \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines
+% \parskip 0pt plus .5pt
+ \rightskip0pt plus 2.5em
+ \tolerance 10000
+ \hyphenpenalty 10000
+ \parindent0pt
+ \message{Index:}
+ \marking[CWEBfilename] {#1}
+ \marking[CWEBsectiontitle] {index}
+ \marking[CWEBsectionnumber]{}
+ \marking[CWEBsectiondepth]{}
+ \loadCWEBmacros{#1}
+ \theCWEBbeforeindex
+ \ReadFile{#1.idx}\relax
+ \theCWEBafterindex
+ \par
+ \egroup}
+
+\def\processCWEBsections #1 %
+ {\bgroup
+ \resetCWEBcontext
+ \activateCWEB
+ \loadCWEBmacros{#1}
+ \parfillskip = 0pt plus 1fil
+ \parindent = 0pt
+ \let\topsecno=\nullsec
+ \def\note##1%
+ {\quad
+ \bgroup
+ \eightrm
+ ##1~\processCWEBsectionnotes}
+ \def\Q {\CWEBnotesfalse \note{Cited in section}} % crossref for mention of a section
+ \def\Qs{\CWEBnotestrue \note{Cited in sections}} % crossref for mentions of a section
+ \def\U {\CWEBnotesfalse \note{Used in section}} % crossref for use of a section
+ \def\Us{\CWEBnotestrue \note{Used in sections}} % crossref for uses of a section
+ \def\I {\par\hangindent 2em}%
+ \let\*=*
+ \message{Section names:}
+ \marking[CWEBfilename] {#1}
+ \marking[CWEBsectiontitle] {sections}
+ \marking[CWEBsectionnumber]{}
+ \marking[CWEBsectiondepth]{}
+ \loadCWEBmacros{#1}
+ \ReadFile{#1.scn}\relax
+ \par
+ \botofcontents
+ \par
+ \egroup}
+
+\def\processCWEBcontents #1 %
+ {\bgroup
+ \resetCWEBcontext
+ \activateCWEB
+ \loadCWEBmacros{#1}
+ \rightskip = 0pt
+ \hyphenpenalty = 50
+ \tolerance = 200
+ \parindent = 0pt
+ \line{\hfil Section\hbox to3em{\hss Page}}
+ \let\ZZ=\contentsline
+ \message{Table of contents:}
+ \marking[CWEBfilename] {#1}
+ \marking[CWEBsectiontitle] {table of contents}
+ \marking[CWEBsectionnumber]{}
+ \marking[CWEBsectiondepth]{}
+ \loadCWEBmacros{#1}
+ \ReadFile{#1.toc}\relax
+ \par
+ \egroup}
+
+\defCWEBmacro\contentsline#1#2#3#4#5%
+ {\ifnum#2=0
+ \smallbreak
+ \fi
+ \line{\consetup{#2}#1
+ \rm
+ \leaders\hbox to .5em{.\hfil}\hfil\
+ {\localcolortrue\goto{#3}[web:#3]}% below: \gotorealpage ? should be changed
+ \hbox to3em{\localcolortrue\hss\gotorealpage{}{}{#5}{\translatednumber[#4]\presetgoto}}}}
+
+%D A last hack, needed because a file can overload of the
+%D above. (Some day: a check like \type{\ifx#1\CWEBdefined}.)
+
+\def\outer#1#2%
+ {\ifx#2\undefined
+ \expandafter#1\expandafter#2%
+ \else
+ \expandafter#1\expandafter\ThrowAway
+ \fi}
+
+\endinput
diff --git a/tex/context/modules/mkiv/m-database.lua b/tex/context/modules/mkiv/m-database.lua
new file mode 100644
index 000000000..91e9636ee
--- /dev/null
+++ b/tex/context/modules/mkiv/m-database.lua
@@ -0,0 +1,132 @@
+if not modules then modules = { } end modules ['m-database'] = {
+ version = 1.001,
+ comment = "companion to m-database.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local sub, gmatch = string.sub, string.gmatch
+local concat = table.concat
+local lpegpatterns, lpegmatch, lpegsplitat = lpeg.patterns, lpeg.match, lpeg.splitat
+local lpegP, lpegC, lpegS, lpegCt, lpegCc, lpegCs = lpeg.P, lpeg.C, lpeg.S, lpeg.Ct, lpeg.Cc, lpeg.Cs
+local stripstring = string.strip
+
+moduledata.database = moduledata.database or { }
+moduledata.database.csv = moduledata.database.csv or { }
+
+-- One also needs to enable context.trace, here we only plug in some code (maybe
+-- some day this tracker will also toggle the main context tracer.
+
+local trace_flush = false trackers.register("module.database.flush", function(v) trace_flush = v end)
+local report_database = logs.reporter("database")
+
+local context = context
+
+local l_tab = lpegpatterns.tab
+local l_space = lpegpatterns.space
+local l_comma = lpegpatterns.comma
+local l_empty = lpegS("\t\n\r ")^0 * lpegP(-1)
+
+local v_yes = interfaces.variables.yes
+
+local separators = { -- not interfaced
+ tab = l_tab,
+ tabs = l_tab^1,
+ comma = l_comma,
+ space = l_space,
+ spaces = l_space^1,
+}
+
+function moduledata.database.csv.process(settings)
+ local data
+ if settings.type == "file" then
+ local filename = resolvers.finders.byscheme("any",settings.database)
+ data = filename ~= "" and io.loaddata(filename)
+ data = data and string.splitlines(data)
+ else
+ data = buffers.getlines(settings.database)
+ end
+ if data and #data > 0 then
+ local catcodes = tonumber(settings.catcodes) or tex.catcodetable
+ context.pushcatcodes(catcodes)
+ if trace_flush then
+ context.pushlogger(report_database)
+ end
+ local separatorchar, quotechar, commentchar = settings.separator, settings.quotechar, settings.commentchar
+ local before, after = settings.before or "", settings.after or ""
+ local first, last = settings.first or "", settings.last or ""
+ local left, right = settings.left or "", settings.right or ""
+ local setups = settings.setups or ""
+ local strip = settings.strip == v_yes or false
+ local command = settings.command or ""
+ separatorchar = (not separatorchar and ",") or separators[separatorchar] or separatorchar
+ local separator = type(separatorchar) == "string" and lpegS(separatorchar) or separatorchar
+ local whatever = lpegC((1 - separator)^0)
+ if quotechar and quotechar ~= "" then
+ local quotedata = nil
+ for chr in gmatch(quotechar,".") do
+ local quotechar = lpegP(chr)
+ local quoteword = lpegCs(((l_space^0 * quotechar)/"") * (1 - quotechar)^0 * ((quotechar * l_space^0)/""))
+ if quotedata then
+ quotedata = quotedata + quoteword
+ else
+ quotedata = quoteword
+ end
+ end
+ whatever = quotedata + whatever
+ end
+ local checker = commentchar ~= "" and lpegS(commentchar)
+ if strip then
+ whatever = whatever / stripstring
+ end
+ if left ~= "" then
+ whatever = lpegCc(left) * whatever
+ end
+ if right ~= "" then
+ whatever = whatever * lpegCc(right)
+ end
+ if command ~= "" then
+ whatever = lpegCc("{") * whatever * lpegCc("}")
+ end
+ whatever = whatever * (separator/"" * whatever)^0
+ if first ~= "" then
+ whatever = lpegCc(first) * whatever
+ end
+ if last ~= "" then
+ whatever = whatever * lpegCc(last)
+ end
+ if command ~= "" then
+ whatever = lpegCs(lpegCc(command) * whatever)
+ else
+ whatever = lpegCs(whatever)
+ end
+ local found = false
+ for i=1,#data do
+ local line = data[i]
+ if not lpegmatch(l_empty,line) and (not checker or not lpegmatch(checker,line)) then
+ if not found then
+ if setups ~= "" then
+ context.begingroup()
+ context.setups { setups }
+ end
+ context(before)
+ found = true
+ end
+ context(lpegmatch(whatever,line))
+ end
+ end
+ if found then
+ context(after)
+ if setups ~= "" then
+ context.endgroup()
+ end
+ end
+ context.popcatcodes()
+ if trace_flush then
+ context.poplogger()
+ end
+ else
+ -- message
+ end
+end
diff --git a/tex/context/modules/mkiv/m-database.mkiv b/tex/context/modules/mkiv/m-database.mkiv
new file mode 100644
index 000000000..cc7dd3d72
--- /dev/null
+++ b/tex/context/modules/mkiv/m-database.mkiv
@@ -0,0 +1,211 @@
+%D \module
+%D [ file=m-database,
+%D version=2010.08.04,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Database Thingies,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D For Mojca.
+
+\registerctxluafile{m-database}{1.001}
+
+\unprotect
+
+\definenamespace
+ [db]
+ [type=module,
+ comment=database module,
+ version=1,
+ name=database,
+ parent=db,
+ setup=yes,
+ command=yes]
+
+\setupdatabase
+ [\c!separator={,},
+ \c!quotechar=,
+ \c!commentchar=,
+ \c!strip=\v!no,
+ \c!before=,
+ \c!after=,
+ \c!first=,
+ \c!last=,
+ \c!left=,
+ \c!right=]
+
+% \let\currentdatabase\empty \the\everypresetdatabase % or just:
+
+\setupdatabase
+ [\c!separator={,}]
+
+\def\module_database_process#1#2#3%
+ {\edef\currentdatabasetype{#1}%
+ \edef\currentdatabase {#2}%
+ \edef\currentdatabasename{#3}%
+ \ifx\currentdatabasename\empty
+ \let\currentdatabasename\currentdatabase
+ \let\currentdatabase\empty
+ \fi
+ \ctxlua{moduledata.database.csv.process {
+ name = "\currentdatabase",
+ type = "\currentdatabasetype",
+ database = "\currentdatabasename",
+ strip = "\databaseparameter\c!strip",
+ separator = \!!bs\databaseparameter\c!separator \!!es,
+ quotechar = \!!bs\databaseparameter\c!quotechar \!!es,
+ commentchar = \!!bs\databaseparameter\c!commentchar\!!es,
+ setups = \!!bs\databaseparameter\c!setups \!!es,
+ before = \!!bs\databaseparameter\c!before \!!es,
+ after = \!!bs\databaseparameter\c!after \!!es,
+ first = \!!bs\databaseparameter\c!first \!!es,
+ last = \!!bs\databaseparameter\c!last \!!es,
+ left = \!!bs\databaseparameter\c!left \!!es,
+ right = \!!bs\databaseparameter\c!right \!!es,
+ command = \!!bs\databaseparameter\c!command \!!es,
+ catcodes = \number\catcodetable
+ }}}
+
+\unexpanded\def\processdatabasebuffer{\dodoubleempty\module_database_process_buffer}
+\unexpanded\def\processdatabasefile {\dodoubleempty\module_database_process_file}
+
+\def\module_database_process_buffer[#1][#2]{\module_database_process{buffer}{#1}{#2}}
+\def\module_database_process_file [#1][#2]{\module_database_process{file} {#1}{#2}}
+
+% for old times sake:
+
+\unexpanded\def\defineseparatedlist {\dodoubleempty\module_database_separated_list_define}
+\unexpanded\def\processseparatedfile{\dodoubleempty\module_database_separated_list_process}
+
+\def\module_database_separated_list_define[#1][#2]%
+ {\definedatabase[#1][#2]%
+ \setuvalue{\e!start#1}{\grabbufferdatadirect{#1}{\e!start#1}{\e!stop#1}}%
+ \setuvalue{\e!stop#1}{\processdatabasebuffer[#1][#1]}}
+
+\def\module_database_separated_list_process[#1][#2]%
+ {\processdatabasefile[#1][#2]}
+
+\unexpanded\def\startseparatedlist[#1]% to be interfaced
+ {\unexpanded\def\stopseparatedlist{\processdatabasebuffer[#1][#1]}%
+ \grabbufferdatadirect{#1}{startseparatedlist}{stopseparatedlist}}
+
+\let\setupseparatedlist\setupdatabase
+
+\protect
+
+\continueifinputfile{m-database.mkiv}
+
+\starttext
+
+% m-database.txt
+%
+% 1,2,3,4,5
+% 6,7,8,"9,x",0
+% A,B,C,D
+% E,,F
+% G
+
+\definedatabase[test]
+
+\setupdatabase
+ [test]
+ [separator={,},
+ quotechar={"},
+ before={<},
+ after={>},
+ first={\endgraf[},
+ last={]\endgraf},
+ left={ (},
+ right={) }]
+
+\startbuffer[testbuffer]
+1,2,3,4,5
+6,7,8,"9,x",0
+A,B,C,D
+E,,F
+G
+\stopbuffer
+
+\processdatabasebuffer[test][testbuffer]
+
+\processdatabasefile[test][m-database.txt]
+
+\defineseparatedlist
+ [CSV]
+ [separator={,},
+ before=\bTABLE,after=\eTABLE,
+ first=\bTR,last=\eTR,
+ left=\bTD,right=\eTD]
+
+% \startseparatedlist[CSV]
+% a,b,c
+% d,e,f
+% \stopseparatedlist
+
+\startCSV
+a,b,c
+d,e,f
+\stopCSV
+
+\defineseparatedlist
+ [CSV]
+ [separator={,+},quotechar={"'},commentchar=\letterhash,
+ before={\starttabulate[|l|l|l|]},after=\stoptabulate,
+ first=\NC,last=\NR,
+ left=,right=\NC]
+
+\startCSV
+#a,b,"c,d"
+a,b,"c,d"
+a,'b,c',d
+"a,b"+c+d
+\stopCSV
+
+\defineseparatedlist
+ [CSV]
+ [separator=space,
+ first=\NC,last=\NR,
+ left=,right=\NC,
+ before={\starttabulate[|l|l|l|]},after=\stoptabulate]
+
+\startCSV
+a b c
+d e f
+\stopCSV
+
+\startsetups csv:unix
+ \catcode\hashasciicode\commentcatcode
+\stopsetups
+
+\defineseparatedlist
+ [CSV]
+ [setups=csv:unix,
+ separator={,},
+ first=\NC,last=\NR,
+ left=,right=\NC,
+ before={\starttabulate[|l|l|l|]},after=\stoptabulate]
+
+\processseparatedfile[CSV][m-database.txt]
+
+\defineseparatedlist[CSV]
+ [separator=comma,
+ before=\bTABLE, after=\eTABLE,
+ first=\bTR, last=\eTR,
+ left=\bTD, right=\eTD]
+
+\startCSV
+a,b,c,č
+d,e,f,š
+\stopCSV
+
+\startseparatedlist[CSV]
+a,b,c,č
+d,e,f,š
+\stopseparatedlist
+
+\stoptext
diff --git a/tex/context/modules/mkiv/m-directives.mkiv b/tex/context/modules/mkiv/m-directives.mkiv
new file mode 100644
index 000000000..8b551b27a
--- /dev/null
+++ b/tex/context/modules/mkiv/m-directives.mkiv
@@ -0,0 +1,3 @@
+\starttext
+ \showdirectives
+\stoptext
diff --git a/tex/context/modules/mkiv/m-educat.mkiv b/tex/context/modules/mkiv/m-educat.mkiv
new file mode 100644
index 000000000..93b1a6c5d
--- /dev/null
+++ b/tex/context/modules/mkiv/m-educat.mkiv
@@ -0,0 +1,217 @@
+%D \module
+%D [ file=m-educat,
+%D version=2003.03.05,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=Educational Extras,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This module will collect a few macros cq.\ definitions
+%D meant for educational use. Most of the code has been laying
+%D around for some time and has been (or is still) used in
+%D \PRAGMA\ projects.
+
+\unprotect
+
+\definesystemvariable{iv}
+
+\definecolor [answerareacolor] [s=.90]
+\definecolor [answerlinecolor] [white]
+
+\definetextbackground
+ [\v!answerarea]
+ [\c!location=\v!text,
+ \c!n=0,
+ \c!frame=\v!off,
+ \c!framecolor=answerlinecolor,
+ \c!rulethickness=2pt,
+ \c!background=\v!color,
+ \c!backgroundcolor=answerareacolor,
+ \c!alternative=2] % betweenline
+
+% n=gedwongen
+% m=extra auto
+
+\def\setupanswerarea
+ {\setuptextbackground[\v!answerarea]}
+
+\setvalue\e!answerspace
+ {\dosingleempty\doanswerspace}
+
+\def\doanswerspace[#1]%
+ {\begingroup
+ \dontcomplain
+ \setupanswerarea
+ [\c!n=0,\c!m=,#1,\c!location=\v!text]%
+ \doifelsenothing{\textbackgroundparameter\c!m}
+ {\expandafter\donoanswerspace}
+ {\expandafter\dodoanswerspace}%
+ [#1]}
+
+\def\donoanswerspace[#1]#2%
+ {\setupthinrules
+ [\c!alternative=\textbackgroundparameter\c!alternative,
+ \c!color=\textbackgroundparameter\c!framecolor,
+ \c!background=\textbackgroundparameter\c!background,
+ \c!backgroundcolor=\textbackgroundparameter\c!backgroundcolor,
+ \c!rulethickness=\textbackgroundparameter\c!rulethickness]%
+ \doifelse{\textbackgroundparameter\c!n}{*}
+ {\thinrule
+ \par}
+ {\scratchcounter0\textbackgroundparameter\c!n\relax
+ % tricky, guess
+ \def\processisolatedword##1%
+ {\setbox\scratchbox=\hbox{##1}%
+ \vbox{\hsize\wd\scratchbox\thinrule
+ \ifcase\scratchcounter\else
+ \setbox\scratchbox=\hbox{\space}%
+ \nobreak\hskip\zeropoint \!!minus \wd\scratchbox
+ \vbox{\hsize\wd\scratchbox\thinrule}%
+ \fi}}%
+ \processisolatedwords{#2}\processisolatedword
+ % so far
+ \ifcase\scratchcounter \else \ifnum\scratchcounter<3
+ \nobreak \vbox{\hsize\scratchcounter em\thinrule}%
+ \else % more
+ \advance \scratchcounter -2
+ \dorecurse\scratchcounter{\allowbreak\vbox{\hsize1em\thinrule}}%
+ \nobreak \vbox{\hsize2em\thinrule}%
+ \fi \fi}%
+ \endgroup}
+
+\def\dodoanswerspace[#1]#2% m case
+ {\getvalue{\e!start\v!answerarea}%
+ #2%
+ \doifelse{\textbackgroundparameter\c!m}{*}
+ {\hfill\strut
+ \getvalue{\e!stop\v!answerarea}%
+ \par}
+ {\scratchcounter0\textbackgroundparameter\c!m\relax
+ \ifcase\scratchcounter \else \ifnum\scratchcounter<3
+ \nobreak \hbox to \scratchcounter em{\strut\hss}%
+ \else % more
+ \advance \scratchcounter -2
+ \dorecurse\scratchcounter{\allowbreak\hbox to 1em{\strut\hss}}%
+ \nobreak \hbox to 2em{\strut\hss}%
+ \fi \fi
+ \getvalue{\e!stop\v!answerarea}}%
+ \endgroup}
+
+\setvalue{\e!start\e!answerlines}%
+ {\dosingleempty\dostartanswerlines}
+
+\def\dostartanswerlines[#1]%
+ {\begingroup
+ \dontcomplain
+ \setupanswerarea
+ [\c!n=0,\c!m=,#1,\c!location=\v!text]%
+ \doifnot{\textbackgroundparameter\c!option}\v!joinedup\softbreak
+ \doifelsenothing{\textbackgroundparameter\c!m}
+ {\expandafter\donostartanswerlines}
+ {\expandafter\dodostartanswerlines}%
+ [#1]}
+
+\def\donostartanswerlines[#1]%
+ {\setupthinrules
+ [\c!alternative=\textbackgroundparameter\c!alternative,
+ \c!color=\textbackgroundparameter\c!framecolor,
+ \c!background=\textbackgroundparameter\c!background,
+ \c!backgroundcolor=\textbackgroundparameter\c!backgroundcolor,
+ \c!rulethickness=\textbackgroundparameter\c!rulethickness]%
+ \thinrules[\c!n=\textbackgroundparameter\c!n]\par
+ \endgroup
+ \grabuntil{\e!stop\e!answerlines}}
+
+\def\dodostartanswerlines[#1]%
+ {\begingroup
+ \getvalue{\e!start\v!answerarea}%
+ \ignorespaces}
+
+\setvalue{\e!stop\e!answerlines}%
+ {\scratchcounter0\textbackgroundparameter\c!m\relax
+ % a \softbreak is more efficient in pos dan \par
+ \ifcase\scratchcounter
+ % nothing
+ \or
+ \softbreak
+ \else
+ \softbreak
+ \advance \scratchcounter \minusone
+ \dorecurse\scratchcounter{\strut\hfill\strut\softbreak}%
+ \fi
+ \strut\hfill\strut
+ \getvalue{\e!stop\v!answerarea}%
+ \par\endgroup\endgroup}
+
+\setvalue\e!answerlines
+ {\dosingleempty\doanswerlines}
+
+\def\doanswerlines[#1]#2%
+ {\getvalue{\e!start\e!answerlines}[#1]%
+ #2%
+ \getvalue{\e!stop\e!answerlines}}
+
+\protect \doifnotmode{demo}{\endinput}
+
+%D Test materiaal.
+
+\starttext
+
+\startnotmode[answers]
+
+ \setupanswerarea[level=+1]
+
+\stopnotmode
+
+\setupcolors[state=start]
+
+test test test \answerspace [n=10] {Whow}. test test test
+test tets test test \answerspace [n=10] {Whow}. test test
+test test tets test test \answerspace [n=10] {Whow}. test
+test test test tets test test \answerspace [n=10] {Whow}.
+test test test test test test \answerspace [n=*] {Whow.}
+
+test test test test test test test \startanswerlines
+[n=3] What A Junk Answer \stopanswerlines
+
+test test test test test test test \startanswerlines
+[n=3,alternative=0] What A Junk Answer \stopanswerlines
+
+test test test test test test test \startanswerlines
+[n=3,alternative=1] What A Junk Answer \stopanswerlines
+
+test test test test test test test \startanswerlines
+[n=3,alternative=2] What A Junk Answer \stopanswerlines
+
+\startitemize[paragraph]
+\item \startanswerlines [option=seried,n=2] xxx \stopanswerlines
+\stopitemize
+
+test test test \answerspace [m=10] {Whow}. test test test
+test tets test test \answerspace [m=10] {Whow}. test test
+test test tets test test \answerspace [m=10] {Whow}. test
+test test test tets test test \answerspace [m=10] {Whow}.
+test test test test test test \answerspace [m=*] {Whow.}
+
+test test test test test test test \startanswerlines
+[m=2] What A Junk Answer \stopanswerlines
+
+test test test test test test test \startanswerlines
+[m=2,alternative=0] What A Junk Answer \stopanswerlines
+
+test test test test test test test \startanswerlines
+[m=2,alternative=1] What A Junk Answer \stopanswerlines
+
+test test test test test test test \startanswerlines
+[m=2,alternative=2] What A Junk Answer \stopanswerlines
+
+\startitemize[paragraph]
+\item \startanswerlines [option=seried,m=2] xxx \stopanswerlines
+\stopitemize
+
+\stoptext
diff --git a/tex/context/modules/mkiv/m-escrito.lua b/tex/context/modules/mkiv/m-escrito.lua
new file mode 100644
index 000000000..0d7a04741
--- /dev/null
+++ b/tex/context/modules/mkiv/m-escrito.lua
@@ -0,0 +1,7088 @@
+if not modules then modules = { } end modules ['m-escrito'] = {
+ version = 1.001,
+ comment = "companion to m-escrito.mkiv",
+ author = "Taco Hoekwater (BitText) and Hans Hagen (PRAGMA-ADE)",
+ license = "see below and context related readme files"
+}
+
+-- This file is derived from Taco's escrito interpreter. Because the project was
+-- more or less stopped, after some chatting we decided to preserve the result
+-- and make it useable in ConTeXt. Hans went over all code, fixed a couple of
+-- things, messed other things, made the code more efficient, wrapped all in
+-- some helpers. So, a diff between the original and this file is depressingly
+-- large. This means that you shouldn't bother Taco with the side effects (better
+-- or worse) that result from this.
+
+-- Fonts need some work and I will do that when needed. I might cook up something
+-- similar to what we do with MetaFun. First I need to run into a use case. After
+-- all, this whole exercise is just that: getting an idea of what processing PS
+-- code involves.
+
+-- Here is the usual copyright blabla:
+--
+-- Copyright 2010 Taco Hoekwater <taco@luatex.org>. All rights reserved.
+--
+-- Redistribution and use in source and binary forms, with or without modification,
+-- are permitted provided that the following conditions are met:
+--
+-- 1. Redistributions of source code must retain the above copyright notice, this
+-- list of conditions and the following disclaimer.
+--
+-- 2. Redistributions in binary form must reproduce the above copyright notice, this
+-- list of conditions and the following disclaimer in the documentation and/or
+-- other materials provided with the distribution.
+--
+-- THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY EXPRESS OR
+-- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+-- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+-- SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+-- OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+-- DAMAGE.
+
+-- We use a couple of do..ends later on because this rather large file has too many
+-- locals otherwise. Possible optimizations are using insert/remove and getting rid
+-- of the VM calls (in direct mode they are no-ops anyway). We can also share some
+-- more code here and there.
+
+local type, unpack, tonumber, tostring, next = type, unpack, tonumber, tostring, next
+
+local format = string.format
+local gmatch = string.gmatch
+local match = string.match
+local sub = string.sub
+local char = string.char
+local byte = string.byte
+
+local insert = table.insert
+local remove = table.remove
+local concat = table.concat
+local reverse = table.reverse
+
+local abs = math.abs
+local ceil = math.ceil
+local floor = math.floor
+local sin = math.sin
+local cos = math.cos
+local rad = math.rad
+local sqrt = math.sqrt
+local atan2 = math.atan2
+local tan = math.tan
+local deg = math.deg
+local pow = math.pow
+local log = math.log
+local log10 = math.log10
+local random = math.random
+local setranseed = math.randomseed
+
+local bitand = bit32.band
+local bitor = bit32.bor
+local bitxor = bit32.bxor
+local bitrshift = bit32.rshift
+local bitlshift = bit32.lshift
+
+local lpegmatch = lpeg.match
+local Ct, Cc, Cs, Cp, C, R, S, P, V = lpeg.Ct, lpeg.Cc, lpeg.Cs, lpeg.Cp, lpeg.C, lpeg.R, lpeg.S, lpeg.P, lpeg.V
+
+local formatters = string.formatters
+local setmetatableindex = table.setmetatableindex
+
+-- Namespace
+
+-- HH: Here we assume just one session. If needed we can support more (just a matter
+-- of push/pop) but it makes the code more complex and less efficient too.
+
+escrito = { }
+
+----- escrito = escrito
+local initializers = { }
+local devices = { }
+local specials
+
+local DEBUG = false -- these will become trackers if needed
+local INITDEBUG = false -- these will become trackers if needed
+local MAX_INT = 0x7FFFFFFF -- we could have slightly larger ints because lua internally uses doubles
+
+initializers[#initializers+1] = function(reset)
+ if reset then
+ specials = nil
+ else
+ specials = { }
+ end
+end
+
+local devicename
+local device
+
+-- "boundingbox",
+-- "randomseed",
+
+-- Composite objects
+--
+-- Arrays, dicts and strings are stored in VM. To do this, VM is an integer-indexed table. This appears
+-- a bit silly in lua because we are actually just emulating a C implementation detail (pointers) but it
+-- is documented behavior. There is also supposed to be a VM stack, but I will worry about that when it
+-- becomes time to implement save/restore. (TH)
+
+local VM -- todo: just a hash
+
+initializers[#initializers+1] = function()
+ VM = { }
+end
+
+local directvm = true
+
+local add_VM, get_VM
+
+if directvm then -- if ok then we remove the functions
+
+ add_VM = function(a)
+ return a
+ end
+ get_VM = function(i)
+ return i
+ end
+
+else
+
+ add_VM = function(a)
+ local n = #VM + 1
+ VM[n] = a
+ return n
+ end
+
+ get_VM = function(i)
+ return VM[i]
+ end
+
+end
+
+-- Execution stack
+
+local execstack
+local execstackptr
+local do_exec
+local next_object
+local stopped
+
+initializers[#initializers+1] = function()
+ execstack = { }
+ execstackptr = 0
+ stopped = false
+end
+
+local function pop_execstack()
+ if execstackptr > 0 then
+ local value = execstack[execstackptr]
+ execstackptr = execstackptr - 1
+ return value
+ else
+ return nil -- stackunderflow
+ end
+end
+
+local function push_execstack(v)
+ execstackptr = execstackptr + 1
+ execstack[execstackptr] = v
+end
+
+-- Operand stack
+--
+-- Most operand and exec stack entries are four-item arrays:
+--
+-- [1] = "[integer|real|boolean|name|mark|null|save|font]" (a postscript interpreter type)
+-- [2] = "[unlimited|read-only|execute-only|noaccess]"
+-- [3] = "[executable|literal]" (exec attribute)
+-- [4] = value (a VM index inthe case of names)
+--
+-- But there are some exceptions.
+--
+-- Dictionaries save the access attribute inside the value
+--
+-- [1] = "dict"
+-- [2] = irrelevant
+-- [3] = "[executable|literal]"
+-- [4] = value (a VM index)
+--
+-- Operators have a fifth item:
+--
+-- [1] = "operator"
+-- [2] = "[unlimited|read-only|execute-only|noaccess]"
+-- [3] = "[executable|literal]"
+-- [4] = value
+-- [5] = identifier (the operator name)
+--
+-- Strings and files have a fifth and a sixth item, the fifth of which is
+-- only relevant if the exec attribute is 'executable':
+--
+-- [1] = "[string|file]"
+-- [2] = "[unlimited|read-only|execute-only|noaccess]"
+-- [3] = "[executable|literal]"
+-- [4] = value (a VM index) (for input files, this holds the whole file)
+-- [5] = exec-index
+-- [6] = length
+-- [7] = iomode (for files only)
+-- [8] = filehandle (for files only)
+--
+-- Arrays also have a seven items, the fifth is only relevant if
+-- the exec attribute is 'executable', and the seventh is used to differentiate
+-- between direct and indirect interpreter views of the object.
+--
+-- [1] = "array"
+-- [2] = "[unlimited|read-only|execute-only|noaccess]"
+-- [3] = "[executable|literal]"
+-- [4] = value (a VM index)
+-- [5] = exec-index
+-- [6] = length (a VM index)
+-- [7] = "[d|i]" (direct vs. indirect)
+--
+-- The exec stack also has an object with [1] == ".stopped", which is used
+-- for "stopped" execution contexts
+
+local opstack
+local opstackptr
+
+initializers[#initializers+1] = function()
+ opstack = { }
+ opstackptr = 0
+end
+
+local function pop_opstack()
+ if opstackptr > 0 then
+ local value = opstack[opstackptr]
+ opstackptr = opstackptr - 1
+ return value
+ else
+ return nil -- stackunderflow
+ end
+end
+
+local function push_opstack(v)
+ opstackptr = opstackptr + 1
+ opstack[opstackptr] = v
+end
+
+local function check_opstack(n)
+ return opstackptr >= n
+end
+
+local function get_opstack()
+ if opstackptr > 0 then
+ return opstack[opstackptr]
+ else
+ return nil -- stackunderflow
+ end
+end
+
+-- In case of error, the interpreter has to restore the opstack
+
+local function copy_opstack()
+ local t = { }
+ for n=1,opstackptr do
+ local sn = opstack[n]
+ t[n] = { unpack(sn) }
+ end
+ return t
+end
+
+local function set_opstack(new)
+ opstackptr = #new
+ opstack = new
+end
+
+-- Dict stack
+
+local dictstack
+local dictstackptr
+
+initializers[#initializers+1] = function()
+ dictstack = { }
+ dictstackptr = 0
+end
+
+-- this finds a name in the current dictionary stack
+
+local function lookup(name)
+ for n=dictstackptr,1,-1 do
+ local found = get_VM(dictstack[n])
+ if found then
+ local dict = found.dict
+ if dict then
+ local d = dict[name]
+ if d then
+ return d, n
+ end
+ end
+ end
+ end
+ return nil
+end
+
+-- Graphics state stack
+
+-- device backends are easier if gsstate items use bare data instead of
+-- ps objects, much as possible
+
+-- todo: just use one color array
+
+local gsstate
+
+initializers[#initializers+1] = function(reset)
+ if reset then
+ gsstate = nil
+ else
+ gsstate = {
+ matrix = { 1, 0, 0, 1, 0, 0 },
+ color = {
+ gray = 0,
+ hsb = { },
+ rgb = { },
+ cmyk = { },
+ type = "gray"
+ },
+ position = { }, -- actual x and y undefined
+ path = { },
+ clip = { },
+ font = nil,
+ linewidth = 1,
+ linecap = 0,
+ linejoin = 0,
+ screen = nil, -- by default, we don't use a screen, which matches "1 0 {pop}"
+ transfer = nil, -- by default, we don't have a transfer function, which matches "{}"
+ flatness = 0,
+ miterlimit = 10,
+ dashpattern = { },
+ dashoffset = 0,
+ }
+ end
+end
+
+local function copy_gsstate()
+ local old = gsstate
+ local position = old.position
+ local matrix = old.matrix
+ local color = old.color
+ local rgb = color.rgb
+ local cmyk = color.cmyk
+ local hsb = color.hsb
+ return {
+ matrix = { matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6] },
+ color = {
+ type = color.type,
+ gray = color.gray,
+ hsb = { hsb[1], hsb[2], hsb[3] },
+ rgb = { rgb[1], rgb[2], rgb[3] },
+ cmyk = { cmyk[1], cmyk[2], cmyk[3], cmyk[4] },
+ },
+ position = { position[1], position[2] },
+ path = { unpack (old.path) },
+ clip = { unpack (old.clip) },
+ font = old.font,
+ linewidth = old.linewidth,
+ linecap = old.linecap,
+ linejoin = old.linejoin,
+ screen = old.screen,
+ transfer = nil,
+ flatness = old.flatness,
+ miterlimit = old.miterlimit,
+ dashpattern = { },
+ dashoffset = 0,
+ }
+end
+
+-- gsstack entries are of the form
+-- [1] "[save|gsave]"
+-- [2] {gsstate}
+
+local gsstack
+local gsstackptr
+
+initializers[#initializers+1] = function(reset)
+ if reset then
+ gsstack = nil
+ gsstackptr = nil
+ else
+ gsstack = { }
+ gsstackptr = 0
+ end
+end
+
+local function push_gsstack(v)
+ gsstackptr = gsstackptr + 1
+ gsstack[gsstackptr] = v
+end
+
+local function pop_gsstack()
+ if gsstackptr > 0 then
+ local v = gsstack[gsstackptr]
+ gsstackptr = gsstackptr - 1
+ return v
+ end
+end
+
+-- Currentpage
+
+local currentpage
+
+initializers[#initializers+1] = function(reset)
+ if reset then
+ currentpage = nil
+ else
+ currentpage = { }
+ end
+end
+
+-- Errordict
+
+-- The standard errordict entry. The rest of these dictionaries will be filled
+-- in the new() function.
+
+local errordict
+local dicterror
+
+-- find an error handler
+
+local function lookup_error(name)
+ local dict = get_VM(errordict).dict
+ return dict and dict[name]
+end
+
+-- error handling and reporting
+
+local report = logs.reporter("escrito")
+
+local function ps_error(a)
+ -- can have print hook
+ return false, a
+end
+
+-- Most entries in systemdict are operators, and the operators each have their own
+-- implementation function. These functions are grouped by category cf. the summary
+-- in the Adobe PostScript reference manual, the creation of the systemdict entries
+-- is alphabetical.
+--
+-- In the summary at the start of the operator sections, the first character means:
+--
+-- "-" => todo
+-- "+" => done
+-- "*" => partial
+-- "^" => see elsewhere
+
+local operators = { }
+
+-- Operand stack manipulation operators
+--
+-- +pop +exch +dup +copy +index +roll +clear +count +mark +cleartomark +counttomark
+
+function operators.pop()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ return true
+end
+
+function operators.exch()
+ if opstackptr < 2 then
+ return ps_error('stackunderflow')
+ end
+ local prv = opstackptr-1
+ opstack[opstackptr], opstack[prv] = opstack[prv], opstack[opstackptr]
+ return true
+end
+
+function operators.dup()
+ if opstackptr < 1 then
+ return ps_error('stackunderflow')
+ end
+ local nxt = opstackptr+1
+ opstack[nxt] = opstack[opstackptr]
+ opstackptr = nxt
+ return true
+end
+
+function operators.copy()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if ta == 'integer' then
+ local va = a[4]
+ if va < 0 then
+ return ps_error('typecheck')
+ end
+ local thestack = opstackptr
+ if va > thestack then
+ return ps_error('stackunderflow')
+ end
+ -- use for loop
+ local n = thestack - va + 1
+ while n <= thestack do
+ local b = opstack[n]
+ local tb = b[1]
+ if tb == 'array' or tb == 'string' or tb == 'dict' or tb == 'font' then
+ b = { tb, b[2], b[3], add_VM(get_VM(b[4])), b[5], b[6], b[7] }
+ end
+ push_opstack(b)
+ n = n + 1
+ end
+ elseif ta == 'dict' then
+ local b = a
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'dict' then
+ return ps_error('typecheck')
+ end
+ local thedict = get_VM(b[4])
+ local tobecopied = get_VM(a[4])
+ if thedict.maxsize < tobecopied.size then
+ return ps_error('rangecheck')
+ end
+ if thedict.size ~= 0 then
+ return ps_error('typecheck')
+ end
+ local access = thedict.access
+ if access == 'read-only' or access == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ local dict = { }
+ for k, v in next, tobecopied.dict do
+ dict[k] = v -- fixed, was thedict[a], must be thedict.dict
+ end
+ thedict.access = tobecopied.access
+ thedict.size = tobecopied.size
+ thedict.dict = dict
+ b = { b[1], b[2], b[3], add_VM(thedict) }
+ push_opstack(b)
+ elseif ta == 'array' then
+ local b = a
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'array' then
+ return ps_error('typecheck')
+ end
+ if b[6] < a[6] then
+ return ps_error('rangecheck')
+ end
+ local access = b[2]
+ if access == 'read-only' or access == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ local array = { }
+ local thearray = get_VM(b[4])
+ local tobecopied = get_VM(a[4])
+ for k, v in next, tobecopied do
+ array[k] = v
+ end
+ b = { b[1], b[2], b[3], add_VM(array), a[5], a[6], a[7] } -- fixed, was thearray
+ push_opstack(b)
+ elseif ta == 'string' then
+ local b = a
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'string' then
+ return ps_error('typecheck')
+ end
+ if b[6] < a[6] then
+ return ps_error('rangecheck')
+ end
+ local access = b[2]
+ if access == 'read-only' or access == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ local thestring = get_VM(b[4])
+ local repl = get_VM(a[4])
+ VM[b[4]] = repl .. sub(thestring,#repl+1,-1)
+ b = { b[1], b[2], b[3], add_VM(repl), a[5], b[6] }
+ push_opstack(b)
+ else
+ return ps_error('typecheck')
+ end
+ return true
+end
+
+function operators.index()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if not ta == 'integer' then
+ return ps_error('typecheck')
+ end
+ local n = a[4]
+ if n < 0 then
+ return ps_error('rangecheck')
+ end
+ if n >= opstackptr then
+ return ps_error('stackunderflow')
+ end
+ push_opstack(opstack[opstackptr-n])
+ return true
+end
+
+function operators.roll()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if b[1] ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ if a[1] ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ local stackcount = a[4]
+ if stackcount < 0 then
+ return ps_error('rangecheck')
+ end
+ if stackcount > opstackptr then
+ return ps_error('stackunderflow')
+ end
+ local rollcount = b[4]
+ if rollcount == 0 then
+ return true
+ end
+ if rollcount > 0 then
+ -- can be simplified
+ while rollcount > 0 do
+ local oldtop = opstack[opstackptr]
+ local n = 0
+ while n < stackcount do
+ opstack[opstackptr-n] = opstack[opstackptr-n-1]
+ n = n + 1
+ end
+ opstack[opstackptr-(stackcount-1)] = oldtop
+ rollcount = rollcount - 1
+ end
+ else
+ -- can be simplified
+ while rollcount < 0 do
+ local oldbot = opstack[opstackptr-stackcount+1]
+ local n = stackcount - 1
+ while n > 0 do
+ opstack[opstackptr-n] = opstack[opstackptr-n+1]
+ n = n - 1
+ end
+ opstack[opstackptr] = oldbot
+ rollcount = rollcount + 1
+ end
+ end
+ return true
+end
+
+function operators.clear()
+ opstack = { } -- or just keep it
+ opstackptr = 0
+ return true
+end
+
+function operators.count()
+ push_opstack { 'integer', 'unlimited', 'literal', opstackptr }
+ return true
+end
+
+function operators.mark()
+ push_opstack { 'mark', 'unlimited', 'literal', null }
+end
+
+operators.beginarray = operators.mark
+
+function operators.cleartomark()
+ while opstackptr > 0 do
+ local val = pop_opstack()
+ if not val then
+ return ps_error('unmatchedmark')
+ end
+ if val[1] == 'mark' then
+ return true
+ end
+ end
+ return ps_error('unmatchedmark')
+end
+
+function operators.counttomark()
+ local v = 0
+ for n=opstackptr,1,-1 do
+ if opstack[n][1] == 'mark' then
+ push_opstack { 'integer', 'unlimited', 'literal', v }
+ return true
+ end
+ v = v + 1
+ end
+ return ps_error('unmatchedmark')
+end
+
+-- Arithmetic and math operators
+--
+-- +add +div +idiv +mod +mul +sub +abs +neg +ceiling +floor +round +truncate +sqrt +atan +cos
+-- +sin +exp +ln +log +rand +srand +rrand
+
+function operators.add()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb = a[1], b[1]
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local c = a[4] + b[4]
+ push_opstack {
+ (ta == 'real' or tb == 'real' or c > MAX_INT) and "real" or "integer",
+ 'unlimited', 'literal', c
+ }
+ return true
+end
+
+function operators.sub()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb = a[1], b[1]
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local c = a[4] - b[4]
+ push_opstack {
+ (ta == 'real' or tb == 'real' or c > MAX_INT) and "real" or "integer",
+ 'unlimited', 'literal', c
+ }
+ return true
+end
+
+function operators.div()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb = a[1], b[1]
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local va, vb = a[4], b[4]
+ if vb == 0 then
+ return ps_error('undefinedresult')
+ end
+ push_opstack { 'real', 'unlimited', 'literal', va / vb }
+ return true
+end
+
+function operators.idiv()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb = a[1], b[1]
+ if tb ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ if ta ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ local va, vb = a[4], b[4]
+ if vb == 0 then
+ return ps_error('undefinedresult')
+ end
+ push_opstack { 'integer', 'unlimited', 'literal', floor(va / vb) }
+ return true
+end
+
+function operators.mod()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb = a[1], b[1]
+ if tb ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ if ta ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ local va, vb = a[4], b[4]
+ if vb == 0 then
+ return ps_error('undefinedresult')
+ end
+ local neg = false
+ local v
+ if va < 0 then
+ v = -va
+ neg = true
+ else
+ v = va
+ end
+ local c = v % abs(vb)
+ if neg then
+ c = -c
+ end
+ push_opstack { 'integer', 'unlimited', 'literal', c }
+ return true
+end
+
+function operators.mul()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb = a[1], b[1]
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local c = a[4] * b[4]
+ push_opstack {
+ (ta == 'real' or tb == 'real' or abs(c) > MAX_INT) and 'real' or 'integer',
+ 'unlimited', 'literal', c
+ }
+ return true
+end
+
+function operators.abs()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local v = a[4]
+ local c = abs(v)
+ push_opstack {
+ (ta == 'real' or v == -(MAX_INT+1)) and 'real' or 'integer', -- hm, v or c
+ 'unlimited', 'literal', c
+ }
+ return true
+end
+
+function operators.neg()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local v = a[4]
+ push_opstack {
+ (ta == 'real' or v == -(MAX_INT+1)) and 'real' or 'integer',
+ 'unlimited', 'literal', -v
+ }
+ return true
+end
+
+function operators.ceiling()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local c = ceil(a[4])
+ push_opstack { ta, 'unlimited', 'literal', c }
+ return true
+end
+
+function operators.floor()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local c = floor(a[4])
+ push_opstack { ta, 'unlimited', 'literal', c }
+ return true
+end
+
+function operators.round()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local c = floor(a[4]+0.5)
+ push_opstack { ta, 'unlimited', 'literal', c }
+ return true
+end
+
+function operators.truncate()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local v = a[4]
+ local c =v < 0 and -floor(-v) or floor(v)
+ push_opstack { ta, 'unlimited', 'literal', c }
+ return true
+end
+
+function operators.sqrt()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local v = a[4]
+ if v < 0 then
+ return ps_error('rangecheck')
+ end
+ local c = sqrt(v)
+ push_opstack { 'real', 'unlimited', 'literal', c }
+ return true
+end
+
+function operators.atan()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb = a[1], b[1]
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local va, vb = a[4], b[4]
+ if va == 0 and vb == 0 then
+ return ps_error('undefinedresult')
+ end
+ local c = deg(atan2(rad(va),rad(vb)))
+ if c < 0 then
+ c = c + 360
+ end
+ push_opstack { 'real', 'unlimited', 'literal', c }
+ return true
+end
+
+function operators.sin()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local c = sin(rad(a[4]))
+ -- this is because double calculation introduces a small error
+ if abs(c) < 1.0e-16 then
+ c = 0
+ end
+ push_opstack { 'real', 'unlimited', 'literal', c }
+ return true
+end
+
+function operators.cos()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local c = cos(rad(a[4]))
+ -- this is because double calculation introduces a small error
+ if abs(c) < 1.0e-16 then
+ c = 0
+ end
+ push_opstack { 'real', 'unlimited', 'literal', c }
+ return true
+end
+
+function operators.exp()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb = a[1], b[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ local va, vb = a[4], b[4]
+ if va < 0 and floor(vb) ~= vb then
+ return ps_error('undefinedresult')
+ end
+ local c = pow(va,vb)
+ push_opstack { 'real', 'unlimited', 'literal', c }
+ return true
+end
+
+function operators.ln()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local v = a[4]
+ if v <= 0 then
+ return ps_error('undefinedresult')
+ end
+ local c = log(v)
+ push_opstack { 'real', 'unlimited', 'literal', c }
+ return true
+end
+
+function operators.log()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local v = a[4]
+ if v <= 0 then
+ return ps_error('undefinedresult')
+ end
+ local c = log10(v)
+ push_opstack { 'real', 'unlimited', 'literal', c }
+ return true
+end
+
+escrito.randomseed = os.time()
+
+-- this interval is one off, but that'll do
+
+function operators.rand()
+ local c = random(MAX_INT) - 1
+ push_opstack { 'integer', 'unlimited', 'literal', c }
+ return true
+end
+
+function operators.srand()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if ta ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ escrito.randomseed = a[4]
+ setranseed(escrito.randomseed)
+ return true
+end
+
+function operators.rrand()
+ push_opstack { 'integer', 'unlimited', 'literal', escrito.randomseed }
+ return true
+end
+
+-- Array operators
+--
+-- +array ^[ +] +length +get +put +getinterval +putinterval +aload +astore ^copy +forall
+
+function operators.array()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local t = a[1]
+ local v = a[4]
+ if t ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ if v < 0 then
+ return ps_error('rangecheck')
+ end
+ local array = { }
+ for i=1,v do
+ array[n] = { 'null', 'unlimited', 'literal', true } -- todo: share this one
+ end
+ push_opstack { 'array', 'unlimited', 'literal', add_VM(array), 0, v, 'd'}
+end
+
+function operators.endarray()
+ local n = opstackptr
+ while n > 0 do
+ if opstack[n][1] == 'mark' then
+ break
+ end
+ n = n - 1
+ end
+ if n == 0 then
+ return ps_error('unmatchedmark')
+ end
+ local top = opstackptr
+ local i = opstackptr - n
+ local array = { }
+ while i > 0 do
+ array[i] = pop_opstack()
+ i = i - 1
+ end
+ pop_opstack() -- pop the mark
+ push_opstack { 'array', 'unlimited', 'literal', add_VM(array), #array, #array, 'd' }
+end
+
+function operators.length()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local access = a[2]
+ if access == "noaccess" or access == "executeonly" then
+ return ps_error('invalidaccess')
+ end
+ local ta = a[1]
+ local va = a[4]
+ if ta == "dict" or ta == "font" then
+ va = get_VM(va).size
+ elseif ta == "array" or ta == "string" then
+ va = get_VM(va)
+ va = #va
+ else
+ return ps_error('typecheck')
+ end
+ push_opstack { 'integer', 'unlimited', 'literal', va }
+ return true
+end
+
+function operators.get()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local access = a[2]
+ if access == "noaccess" or access == "execute-only" then
+ return ps_error('invalidaccess')
+ end
+ local ta = a[1]
+ local va = a[4]
+ if ta == "dict" then
+ local dict = get_VM(va)
+ local key = b
+ local tb = b[1]
+ local vb = b[4]
+ if tb == "string" or tb == "name" then
+ key = get_VM(vb)
+ end
+ local ddk = dict.dict[key]
+ if ddk then
+ push_opstack(ddk)
+ else
+ return ps_error('undefined')
+ end
+ elseif ta == "array" then
+ local tb = b[1]
+ local vb = b[4]
+ if tb ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ if vb < 0 or vb >= a[6] then
+ return ps_error('rangecheck')
+ end
+ local array = get_VM(va)
+ local index = vb + 1
+ push_opstack(array[index])
+ elseif ta == "string" then
+ local tb = b[1]
+ local vb = b[4]
+ if tb ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ if vb < 0 or vb >= a[6] then
+ return ps_error('rangecheck')
+ end
+ local thestring = get_VM(va)
+ local index = vb + 1
+ local c = sub(thestring,index,index)
+ push_opstack { 'integer', 'unlimited', 'literal', byte(c) }
+ else
+ return ps_error('typecheck')
+ end
+ return true
+end
+
+function operators.put()
+ local c = pop_opstack()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if ta == "dict" then
+ local dict = get_VM(a[4])
+ if dict.access ~= 'unlimited' then
+ return ps_error('invalidaccess')
+ end
+ local key = b
+ local bt = b[1]
+ if bt == "string" or bt == "name" then
+ key = get_VM(b[4])
+ end
+ local dd = dict.dict
+ local ds = dict.size
+ local ddk = dd[key]
+ if not ddk and (ds == dict.maxsize) then
+ return ps_error('dictfull')
+ end
+ if c[1] == 'array' then
+ c[7] = 'i'
+ end
+ if not ddk then
+ dict.size = ds + 1
+ end
+ dd[key] = c
+ elseif ta == "array" then
+ if a[2] ~= 'unlimited' then
+ return ps_error('invalidaccess')
+ end
+ if b[1] ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ local va, vb = a[4], b[4]
+ if vb < 0 or vb >= a[6] then
+ return ps_error('rangecheck')
+ end
+ local vm = VM[va]
+ local vi = bv + 1
+ if vm[vi][1] == 'null' then
+ a[5] = a[5] + 1
+ end
+ vm[vi] = c
+ elseif ta == "string" then
+ if a[2] ~= 'unlimited' then
+ return ps_error('invalidaccess')
+ end
+ if b[1] ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ if c[1] ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ local va, vb, vc = a[4], b[4], c[4]
+ if vb < 0 or vb >= a[6] then
+ return ps_error('rangecheck')
+ end
+ if vc < 0 or vc > 255 then
+ return ps_error('rangecheck')
+ end
+ local thestring = get_VM(va)
+ VM[va] = sub(thestring,1,vb) .. char(vc) .. sub(thestring,vb+2)
+ else
+ return ps_error('typecheck')
+ end
+ return true
+end
+
+function operators.getinterval()
+ local c = pop_opstack()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb, tc = a[1], b[1], c[1]
+ local aa, ab, ac = a[2], b[2], c[2]
+ local va, vb, vc = a[4], b[4], c[4]
+ if ta ~= "array" and ta ~= 'string' then
+ return ps_error('typecheck')
+ end
+ if tb ~= 'integer' or tc ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ if aa == "execute-only" or aa == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ if vb < 0 or vc < 0 or vb + vc >= a[6] then
+ return ps_error('rangecheck')
+ end
+ -- vb : start
+ -- vc : number
+ if ta == 'array' then
+ local array = get_VM(va)
+ local subarray = { }
+ local index = 1
+ while index <= vc do
+ subarray[index] = array[index+vb]
+ index = index + 1
+ end
+ push_opstack { 'array', aa, a[3], add_VM(subarray), vc, vc, 'd' }
+ else
+ local thestring = get_VM(va)
+ local newstring = sub(thestring,vb+1,vb+vc)
+ push_opstack { 'string', aa, a[3], add_VM(newstring), vc, vc }
+ end
+ return true
+end
+
+function operators.putinterval()
+ local c = pop_opstack()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb, tc = a[1], b[1], c[1]
+ local aa, ab, ac = a[2], b[2], c[2]
+ local va, vb, vc = a[4], b[4], c[4]
+ if ta ~= "array" and ta ~= 'string' then
+ return ps_error('typecheck')
+ end
+ if tc ~= "array" and tc ~= 'string' then
+ return ps_error('typecheck')
+ end
+ if ta ~= tc then
+ return ps_error('typecheck')
+ end
+ if aa ~= "unlimited" then
+ return ps_error('invalidaccess')
+ end
+ if tb ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ if vb < 0 or vb + c[6] >= a[6] then
+ return ps_error('rangecheck')
+ end
+ if ta == 'array' then
+ local newarr = get_VM(vc)
+ local oldarr = get_VM(va)
+ local index = 1
+ local lastindex = c[6]
+ local step = a[5]
+ while index <= lastindex do
+ if oldarr[vb+index][1] == 'null' then
+ a[5] = a[5] + 1 -- needs checking, a[5] not used
+ -- step = step + 1
+ end
+ oldarr[vb+index] = newarr[index]
+ index = index + 1
+ end
+ else
+ local thestring = get_VM(va)
+ VM[va] = sub(thestring,1,vb) .. get_VM(vc) .. sub(thestring,vb+c[6]+1)
+ end
+ return true
+end
+
+function operators.aload()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, aa, va = a[1], a[2], a[4]
+ if ta ~= "array" then
+ return ps_error('typecheck')
+ end
+ if aa == "execute-only" or aa == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ local array = get_VM(va)
+ for i=1,#array do
+ push_opstack(array[i])
+ end
+ push_opstack(a)
+ return true
+end
+
+function operators.astore()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, aa, va = a[1], a[2], a[4]
+ if ta ~= "array" then
+ return ps_error('typecheck')
+ end
+ if aa == "execute-only" or aa == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ local array = get_VM(va)
+ local count = a[6]
+ for i=1,count do
+ local v = pop_opstack()
+ if not v then
+ return ps_error('stackunderflow')
+ end
+ array[i] = v
+ end
+ a[5] = a[5] + count
+ push_opstack(a)
+ return true
+end
+
+function operators.forall()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, aa, va = a[1], a[2], a[4]
+ local tb, ab, vb = b[1], b[2], b[4]
+ if not tb == "array" and b[3] == 'executable' then
+ return ps_error('typecheck')
+ end
+ if tb == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ if not (ta == "array" or ta == 'dict' or ta == 'string' or ta == "font") then
+ return ps_error('typecheck')
+ end
+ if aa == "execute-only" or aa == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ push_execstack { '.exit', 'unlimited', 'literal', false }
+ local curstack = execstackptr
+ if ta == 'array' then
+ if a[6] == 0 then
+ return true
+ end
+ b[7] = 'i'
+ local thearray = get_VM(va)
+ for i=1,#thearray do
+ if stopped then
+ stopped = false
+ return false
+ end
+ push_opstack(thearray[i])
+ b[5] = 1
+ push_execstack(b)
+ while curstack <= execstackptr do
+ do_exec()
+ end
+ end
+ local entry = execstack[execstackptr]
+ if entry[1] == '.exit' and antry[4] == true then
+ pop_execstack()
+ return true
+ end
+ elseif ta == 'dict' or ta == 'font' then
+ local thedict = get_VM(va)
+ if thedict.size == 0 then
+ return true
+ end
+ b[7] = 'i'
+ local thedict = get_VM(va)
+ for k, v in next, thedict.dict do
+ if stopped then
+ stopped = false
+ return false
+ end
+ if type(k) == "string" then
+ push_opstack { 'name', 'unlimited', 'literal', add_VM(k) }
+ else
+ push_opstack(k)
+ end
+ push_opstack(v)
+ b[5] = 1
+ push_execstack(b)
+ while curstack < execstackptr do
+ do_exec()
+ end
+ local entry = execstack[execstackptr]
+ if entry[1] == '.exit' and antry[4] == true then
+ pop_execstack()
+ return true
+ end
+ end
+ else -- string
+ if a[6] == 0 then
+ return true
+ end
+ b[7] = 'i'
+ local thestring = get_VM(va)
+ for v in gmatch(thestring,".") do -- we can use string.bytes
+ if stopped then
+ stopped = false
+ return false
+ end
+ push_opstack { 'integer', 'unlimited', 'literal', byte(v) }
+ b[5] = 1
+ push_execstack(b)
+ while curstack < execstackptr do
+ do_exec()
+ end
+ local entry = execstack[execstackptr]
+ if entry[1] == '.exit' and antry[4] == true then
+ pop_execstack()
+ return true;
+ end
+ end
+ end
+ return true
+end
+
+-- Dictionary operators
+--
+-- +dict ^length +maxlength +begin +end +def +load +store ^get ^put +known +where ^copy
+-- ^forall ^errordict ^systemdict ^userdict +currentdict +countdictstack +dictstack
+
+function operators.dict()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if not a[1] == 'integer' then
+ return ps_error('typecheck')
+ end
+ local s = a[4]
+ if s < 0 then
+ return ps_error('rangecheck')
+ end
+ if s == 0 then -- level 2 feature
+ s = MAX_INT
+ end
+ push_opstack {
+ 'dict',
+ 'unlimited',
+ 'literal',
+ add_VM {
+ access = 'unlimited',
+ size = 0,
+ maxsize = s,
+ dict = { },
+ }
+ }
+end
+
+function operators.maxlength()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, aa, va = a[1], a[2], a[4]
+ if ta ~= 'dict' then
+ return ps_error('typecheck')
+ end
+ if aa == 'execute-only' or aa == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ local thedict = get_VM(va)
+ push_opstack { 'integer', 'unlimited', 'literal', thedict.maxsize }
+end
+
+function operators.begin()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'dict' then
+ return ps_error('typecheck')
+ end
+ dictstackptr = dictstackptr + 1
+ dictstack[dictstackptr] = a[4]
+end
+
+operators["end"] = function()
+ if dictstackptr < 3 then
+ return ps_error('dictstackunderflow')
+ end
+ dictstack[dictstackptr] = nil
+ dictstackptr = dictstackptr - 1
+end
+
+function operators.def()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if not (a[1] == 'name' and a[3] == 'literal') then
+ return ps_error('typecheck')
+ end
+ if b[1] == 'array' then
+ b[7] = 'i'
+ end
+ local thedict = get_VM(dictstack[dictstackptr])
+ if not thedict.dict[get_VM(a[4])] then
+ if thedict.size == thedict.maxsize then
+ -- return ps_error('dictfull') -- level 1 only
+ end
+ thedict.size = thedict.size + 1
+ end
+ thedict.dict[get_VM(a[4])] = b
+ return true
+end
+
+-- unclear: the book says this operator can return typecheck
+
+function operators.load()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local aa = a[2]
+ if aa == 'noaccess' or aa == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ local v = lookup(get_VM(a[4]))
+ if not v then
+ return ps_error('undefined')
+ end
+ push_opstack(v)
+end
+
+function operators.store()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if not (a[1] == 'name' and a[3] == 'literal') then
+ return ps_error('typecheck')
+ end
+ if b[7] == 'array' then
+ b[7] = 'i'
+ end
+ local val, dictloc = lookup(a[4])
+ if val then
+ local thedict = get_VM(dictstack[dictloc])
+ if thedict.access == 'execute-only' or thedict.access == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ thedict.dict[a[4]] = b
+ else
+ local thedict = get_VM(dictstack[dictstackptr])
+ local access = thedict.access
+ local size = thedict.size
+ if access == 'execute-only' or access == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ if size == thedict.maxsize then
+ return ps_error('dictfull')
+ end
+ thedict.size = size + 1
+ thedict.dict[a[4]] = b
+ end
+ return true
+end
+
+function operators.known()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, aa, va = a[1], a[2], a[4]
+ local tb, vb = b[1], b[4]
+ if ta ~= 'dict' then
+ return ps_error('typecheck')
+ end
+ if not (tb == 'name' or tb == 'operator') then
+ return ps_error('typecheck')
+ end
+ if aa == 'noaccess' or aa == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ local thedict = get_VM(va)
+ push_opstack {'boolean', 'unlimited', 'literal', thedict.dict[vb] and true or false }
+ return true
+end
+
+function operators.where()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if not (a[1] == 'name' and a[3] == 'literal') then
+ return ps_error('typecheck')
+ end
+ local val, dictloc = lookup(get_VM(a[4]))
+ local thedict = dictloc and get_VM(dictstack[dictloc]) -- fixed
+ if val then
+ if thedict.access == 'execute-only' or thedict.access == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ push_opstack {'dict', 'unlimited', 'literal', dictstack[dictloc]}
+ push_opstack {'boolean', 'unlimited', 'literal', true}
+ else
+ push_opstack {'boolean', 'unlimited', 'literal', false}
+ end
+ return true
+end
+
+function operators.currentdict()
+ push_opstack { 'dict', 'unlimited', 'literal', dictstack[dictstackptr] }
+ return true
+end
+
+function operators.countdictstack()
+ push_opstack { 'integer', 'unlimited', 'literal', dictstackptr }
+ return true
+end
+
+function operators.dictstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if not a[1] == 'array' then
+ return ps_error('typecheck')
+ end
+ if not a[2] == 'unlimited' then
+ return ps_error('invalidaccess')
+ end
+ if a[6] < dictstackptr then
+ return ps_error('rangecheck')
+ end
+ local thearray = get_VM(a[4])
+ local subarray = { }
+ for i=1,dictstackptr do
+ thearray[n] = { 'dict', 'unlimited', 'literal', dictstack[i] }
+ subarray[n] = thearray[i]
+ end
+ a[5] = a[5] + dictstackptr
+ push_opstack { 'array', 'unlimited', 'literal', add_VM(subarray), dictstackptr, dictstackptr, '' }
+ return true
+end
+
+-- String operators
+--
+-- +string ^length ^get ^put ^getinterval ^putinterval ^copy ^forall +anchorsearch +search
+-- +token
+
+function operators.string()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, va = a[1], a[4]
+ if ta ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ if va < 0 then
+ return ps_error('rangecheck')
+ end
+ push_opstack { 'string', 'unlimited', 'literal', add_VM(''), 1, va }
+end
+
+function operators.anchorsearch()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, aa, va = a[1], a[2], a[4]
+ local tb, ab, vb = b[1], b[2], b[4]
+ if not ta ~= 'string' then
+ return ps_error('typecheck')
+ end
+ if tb ~= 'string' then
+ return ps_error('typecheck')
+ end
+ if aa == 'noaccess' or aa == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ if ab == 'noaccess' or ab == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ local thestring = get_VM(va)
+ local thesearch = get_VM(vb)
+ local prefix = sub(thestring,1,#thesearch)
+ if prefix == thesearch then
+ if aa == 'read-only' then
+ return ps_error('invalidaccess')
+ end
+ local post = sub(thestring,#thesearch+1)
+ push_opstack { 'string', 'unlimited', 'literal', add_VM(post), 1, #post }
+ push_opstack { 'string', 'unlimited', 'literal', add_VM(prefix), 1, #prefix }
+ push_opstack { 'boolean', 'unlimited', 'literal', true }
+ else
+ push_opstack(a)
+ push_opstack { 'boolean', 'unlimited', 'literal', false }
+ end
+ return true
+end
+
+function operators.search()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, aa, va = a[1], a[2], a[4]
+ local tb, ab, vb = b[1], b[2], b[4]
+ if not ta ~= 'string' then
+ return ps_error('typecheck')
+ end
+ if tb ~= 'string' then
+ return ps_error('typecheck')
+ end
+ if aa == 'noaccess' or aa == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ if ab == 'noaccess' or ab == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ local thestring = get_VM(a[4])
+ local thesearch = get_VM(b[4])
+ -- hm, can't this be done easier?
+ local n = 1
+ local match
+ while n + #thesearch-1 <= #thestring do
+ match = sub(thestring,n,n+#thesearch-1)
+ if match == thesearch then
+ break
+ end
+ n = n + 1
+ end
+ if match == thesearch then
+ if aa == 'read-only' then
+ return ps_error('invalidaccess')
+ end
+ local prefix = sub(thestring,1,n-1)
+ local post = sub(thestring,#thesearch+n)
+ push_opstack { 'string', 'unlimited', 'literal', add_VM(post), 1, #post }
+ push_opstack { 'string', 'unlimited', 'literal', add_VM(thesearch), 1, #thesearch }
+ push_opstack { 'string', 'unlimited', 'literal', add_VM(prefix), 1, #prefix }
+ push_opstack { 'boolean', 'unlimited', 'literal', true }
+ else
+ push_opstack(a)
+ push_opstack { 'boolean', 'unlimited', 'literal', false }
+ end
+ return true
+end
+
+function operators.token()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, aa, va = a[1], a[2], a[4]
+ if not (ta == 'string' or ta == 'file') then
+ return ps_error('typecheck')
+ end
+ if aa ~= 'unlimited' then
+ return ps_error('invalidaccess')
+ end
+ -- some fiddling with the tokenization process is needed
+ if ta == 'string' then
+ local top = execstackptr
+ push_execstack { '.token', 'unlimited', 'literal', false }
+ push_execstack { a[1], a[2], 'executable', va, 1, a[6] }
+ local v, err = next_object()
+ if not v then
+ pop_execstack()
+ pop_execstack()
+ push_opstack { 'boolean', 'unlimited', 'literal', false }
+ else
+ local q = pop_execstack()
+ if execstack[execstackptr][1] == '.token' then
+ pop_execstack()
+ end
+ local tq, vq = q[1], q[4]
+ if tq == 'string' and vq ~= va then
+ push_execstack(q)
+ end
+ local thestring, substring
+ if vq ~= va then
+ thestring = ""
+ substring = ""
+ else
+ thestring = get_VM(vq)
+ substring = sub(thestring,q[5] or 0)
+ end
+ push_opstack { ta, aa, a[3], add_VM(substring), 1, #substring}
+ push_opstack(v)
+ push_opstack { 'boolean', 'unlimited', 'literal', true }
+ end
+ else -- file
+ if a[7] ~= 'r' then
+ return ps_error('invalidaccess')
+ end
+ push_execstack { '.token', 'unlimited', 'literal', false }
+ push_execstack { 'file', 'unlimited', 'executable', va, a[5], a[6], a[7], a[8] }
+ local v, err = next_object()
+ if not v then
+ pop_execstack()
+ pop_execstack()
+ push_opstack { 'boolean', 'unlimited', 'literal', false }
+ else
+ local q = pop_execstack() -- the file
+ a[5] = q[5]
+ if execstack[execstackptr][1] == '.token' then
+ pop_execstack()
+ end
+ push_opstack(v)
+ push_opstack { 'boolean', 'unlimited', 'literal', true }
+ end
+ end
+ return true
+end
+
+-- Relational, boolean and bitwise operators
+--
+-- +eq +ne +ge +gt +le +lt +and +not +or +xor ^true ^false +bitshift
+
+local function both()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, aa = a[1], a[2]
+ local tb, ab = b[1], b[2]
+ if aa == 'noaccess' or aa == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ if ab == 'noaccess' or ab == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ if (ta == 'dict' and tb == 'dict') or (ta == 'array' and tb =='array') then
+ return true, a[4], b[4]
+ elseif ((ta == 'string' or ta == 'name') and (tb == 'string' or tb == 'name' )) then
+ local astr = get_VM(a[4])
+ local bstr = get_VM(b[4])
+ return true, astr, bstr
+ elseif ((ta == 'integer' or ta == 'real') and (tb == 'integer' or tb == 'real')) or (ta == tb) then
+ return true, a[4], b[4]
+ else
+ return ps_error('typecheck')
+ end
+ return true
+end
+
+function operators.eq()
+ local ok, a, b = both()
+ if ok then
+ push_opstack { 'boolean', 'unlimited', 'literal', a == b }
+ return true
+ else
+ return a
+ end
+end
+
+function operators.ne()
+ local ok, a, b = both()
+ if ok then
+ push_opstack { 'boolean', 'unlimited', 'literal', a ~= b }
+ return true
+ else
+ return a
+ end
+end
+
+local function both()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local aa, ab = a[2], b[2]
+ if aa == 'noaccess' or aa == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ if ab == 'noaccess' or ab == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ local ta, tb = a[1], b[1]
+ local va, vb = a[4], b[4]
+ if (ta == 'real' or ta == 'integer') and (tb == 'real' or tb == 'integer') then
+ return true, va, vb
+ elseif ta == 'string' and tb == 'string' then
+ local va = get_VM(va)
+ local vb = get_VM(vb)
+ return true, va, vb
+ else
+ return ps_error('typecheck')
+ end
+end
+
+function operators.ge()
+ local ok, a, b = both()
+ if ok then
+ push_opstack { 'boolean', 'unlimited', 'literal', a >= b }
+ return true
+ else
+ return a
+ end
+end
+
+function operators.gt()
+ local ok, a, b = both()
+ if ok then
+ push_opstack { 'boolean', 'unlimited', 'literal', a > b }
+ return true
+ else
+ return a
+ end
+end
+
+function operators.le()
+ local ok, a, b = both()
+ if ok then
+ push_opstack { 'boolean', 'unlimited', 'literal', a <= b }
+ return true
+ else
+ return a
+ end
+end
+
+function operators.lt()
+ local ok, a, b = both()
+ if ok then
+ push_opstack { 'boolean', 'unlimited', 'literal', a < b }
+ return true
+ else
+ return a
+ end
+end
+
+local function both()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local aa, ab = a[2], b[2]
+ if aa == 'noaccess' or aa == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ if ab == 'noaccess' or ab == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ local ta, tb = a[1], b[1]
+ local va, vb = a[4], b[4]
+ if ta == 'boolean' and tb == 'boolean' then
+ return ta, va, vb
+ elseif ta == 'integer' and tb == 'integer' then
+ return ta, va, vb
+ else
+ return ps_error('typecheck')
+ end
+end
+
+operators["and"]= function()
+ local ok, a, b = both()
+ if ok == 'boolean' then
+ push_opstack { 'boolean', 'unlimited', 'literal', a[1] and b[1] }
+ return true
+ elseif ok == 'integer' then
+ push_opstack { 'integer', 'unlimited', 'literal', bitand(a[1],b[1]) }
+ return true
+ else
+ return a
+ end
+end
+
+operators["or"] = function()
+ local ok, a, b = both()
+ if ok == 'boolean' then
+ push_opstack {'boolean', 'unlimited', 'literal', a[1] or b[1] }
+ return true
+ elseif ok == 'integer' then
+ push_opstack {'integer', 'unlimited', 'literal', bitor(a[1],b[1]) }
+ return true
+ else
+ return a
+ end
+end
+
+function operators.xor()
+ local ok, a, b = both()
+ if ok == 'boolean' then
+ push_opstack {'boolean', 'unlimited', 'literal', a[1] ~= b[1] }
+ return true
+ elseif ok == 'integer' then
+ push_opstack {'integer', 'unlimited', 'literal', bitxor(a[1],b[1]) }
+ return true
+ else
+ return a
+ end
+end
+
+operators["not"] = function()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local aa = a[2]
+ local ta = a[1]
+ if aa == 'noaccess' or aa == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ if ta == 'boolean' then
+ push_opstack { 'boolean', 'unlimited', 'literal', not a[4] }
+ elseif ta == 'integer' then
+ push_opstack { 'integer', 'unlimited', 'literal', -a[4] - 1 }
+ else
+ return ps_error('typecheck')
+ end
+ return true
+end
+
+function operators.bitshift()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local aa, ab = a[2], b[2]
+ local ta, tb = a[1], b[1]
+ local va, vb = a[4], b[4]
+ if aa == 'noaccess' or aa == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ if ab == 'noaccess' or ab == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ if not (ta == 'integer' and tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ push_opstack { 'integer', 'unlimited', 'literal', bitrshift(va,vb < 0 and -vb or vb) }
+ return true
+end
+
+-- Control operators
+--
+-- +exec +if +ifelse +for +repeat +loop +exit +stop +stopped +countexecstack +execstack
+-- +quit +start
+
+function operators.exec()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] == 'array' then
+ a[7] = 'i'
+ a[5] = 1
+ end
+ push_execstack(a)
+ return true
+end
+
+operators["if"] = function()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'boolean' then
+ return ps_error('typecheck')
+ end
+ if b[1] ~= 'array' then
+ return ps_error('typecheck')
+ end
+ if a[4] == true then
+ b[7] = 'i'
+ b[5] = 1
+ push_execstack(b)
+ end
+ return true
+end
+
+function operators.ifelse()
+ local c = pop_opstack()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'boolean' then
+ return ps_error('typecheck')
+ end
+ if b[1] ~= 'array' then
+ return ps_error('typecheck')
+ end
+ if c[1] ~= 'array' then
+ return ps_error('typecheck')
+ end
+ if a[4] == true then
+ b[5] = 1
+ b[7] = 'i'
+ push_execstack(b)
+ else
+ c[5] = 1
+ c[7] = 'i'
+ push_execstack(c)
+ end
+ return true
+end
+
+operators["for"] = function()
+ local d = pop_opstack()
+ local c = pop_opstack()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ local ta, tb, tc, td = a[1], b[1], c[1], d[1]
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if not (ta == 'integer' or ta == 'real') then
+ return ps_error('typecheck')
+ end
+ if not (tb == 'integer' or tb == 'real') then
+ return ps_error('typecheck')
+ end
+ if not (tc == 'integer' or tc == 'real') then
+ return ps_error('typecheck')
+ end
+ if not (td == 'array' and d[3] == 'executable') then
+ return ps_error('typecheck')
+ end
+ local initial = a[4]
+ local increment = b[4]
+ local limit = c[4]
+ if initial == limit then
+ return true
+ end
+ push_execstack { '.exit', 'unlimited', 'literal', false }
+ local curstack = execstackptr
+ local tokentype = (a[1] == 'real' or b[1] == 'real' or c[1] == 'real') and 'real' or 'integer'
+ d[7] = 'i'
+ local first, last
+ if increment >= 0 then
+ first, last = initial, limit
+ else
+ first, last = limit, limit
+ end
+ for control=first,last,increment do
+ if stopped then
+ stopped = false
+ return false
+ end
+ push_opstack { tokentype, 'unlimited', 'literal', control }
+ d[5] = 1
+ push_execstack(d)
+ while curstack < execstackptr do
+ do_exec()
+ end
+ local entry = execstack[execstackptr]
+ if entry[1] == '.exit' and entry[4] == true then
+ pop_execstack()
+ return true;
+ end
+ end
+ return true
+end
+
+operators["repeat"] = function()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ if a[4] < 0 then
+ return ps_error('rangecheck')
+ end
+ if not (b[1] == 'array' and b[3] == 'executable') then
+ return ps_error('typecheck')
+ end
+ local limit = a[4]
+ if limit == 0 then
+ return true
+ end
+ push_execstack { '.exit', 'unlimited', 'literal', false }
+ local curstack = execstackptr
+ b[7] = 'i'
+ local control = 0
+ while control < limit do
+ if stopped then
+ stopped = false
+ return false
+ end
+ b[5] = 1
+ push_execstack(b)
+ while curstack < execstackptr do
+ do_exec()
+ end
+ local entry = execstack[execstackptr]
+ if entry[1] == '.exit' and entry[4] == true then
+ pop_execstack()
+ return true;
+ end
+ control = control + 1
+ end
+ return true
+end
+
+function operators.loop()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if not (a[1] == 'array' and a[3] == 'executable') then
+ return ps_error('typecheck')
+ end
+ push_execstack { '.exit', 'unlimited', 'literal', false }
+ local curstack = execstackptr
+ a[7] = 'i'
+ while true do
+ if stopped then
+ stopped = false
+ return false
+ end
+ a[5] = 1
+ push_execstack(a)
+ while curstack < execstackptr do
+ do_exec()
+ end
+ if execstackptr > 0 then
+ local entry = execstack[execstackptr]
+ if entry[1] == '.exit' and entry[4] == true then
+ pop_execstack()
+ return true
+ end
+ end
+ end
+ return true
+end
+
+function operators.exit()
+ local v = pop_execstack()
+ while v do
+ local tv = val[1]
+ if tv == '.exit' then
+ push_execstack { '.exit', 'unlimited', 'literal', true }
+ return true
+ elseif tv == '.stopped' or tv == '.run' then
+ push_execstack(v)
+ return ps_error('invalidexit')
+ end
+ v = pop_execstack()
+ end
+ report("exit without context, quitting")
+ push_execstack { 'operator', 'unlimited', 'executable', operators.quit, "quit" }
+ return true
+end
+
+function operators.stop()
+ local v = pop_execstack()
+ while v do
+ if val[1] == '.stopped' then
+ stopped = true
+ push_opstack { 'boolean', 'unlimited', 'executable', true }
+ return true
+ end
+ v = pop_execstack()
+ end
+ report("stop without context, quitting")
+ push_execstack { 'operator', 'unlimited', 'executable', operators.quit, "quit" }
+ return true
+end
+
+function operators.stopped()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ -- push a special token on the exec stack (handled by next_object):
+ push_execstack { '.stopped', 'unlimited', 'literal', false }
+ a[3] = 'executable'
+ if a[1] == 'array' then
+ a[7] = 'i'
+ a[5] = 1
+ end
+ push_execstack(a)
+ return true
+end
+
+function operators.countexecstack()
+ push_opstack { 'integer', 'unlimited', 'literal', execstackptr }
+ return true
+end
+
+function operators.execstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if not a[1] == 'array' then
+ return ps_error('typecheck')
+ end
+ if not a[2] == 'unlimited' then
+ return ps_error('invalidaccess')
+ end
+ if a[6] < execstackptr then
+ return ps_error('rangecheck')
+ end
+ local thearray = get_VM(a[4])
+ local subarray = { }
+ for n=1,execstackptr do
+ -- thearray[n] = execstack[n]
+ -- subarray[n] = thearray[n]
+ local v = execstack[n]
+ thearray[n] = v
+ subarray[n] = v
+ a[5] = a[5] + 1
+ end
+ push_opstack { 'array', 'unlimited', 'literal', add_VM(subarray), execstackptr, execstackptr, "" }
+ return true
+end
+
+-- clearing the execstack does the trick,
+-- todo: leave open files to be handled by the lua interpreter, for now
+
+function operators.quit()
+ while execstackptr >= 0 do -- todo: for loop / slot 0?
+ execstack[execstackptr] = nil
+ execstackptr = execstackptr - 1
+ end
+ return true
+end
+
+-- does nothing, for now
+
+function operators.start()
+ return true
+end
+
+-- Type, attribute and conversion operators
+--
+-- +type +cvlit +cvx +xcheck +executeonly +noaccess +readonly +rcheck +wcheck +cvi
+-- +cvn +cvr +cvrs +cvs
+
+function operators.type()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ push_opstack { "name", "unlimited", "executable", add_VM(a[1] .. "type") }
+ return true
+end
+
+function operators.cvlit() -- no need to push/pop
+ local a = get_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ a[3] = 'literal'
+ return true
+end
+
+function operators.cvx()
+ local a = get_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ a[3] = 'executable'
+ return true
+end
+
+function operators.xcheck()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ push_opstack { 'boolean', 'unlimited', 'literal', a[3] == 'executable' }
+ return true
+end
+
+function operators.executeonly()
+ local a = pop_opstack() -- get no push
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if ta == 'string' or ta == 'file' or ta == 'array' then
+ if a[2] == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ a[2] = 'execute-only'
+ else
+ return ps_error('typecheck')
+ end
+ push_opstack(a)
+ return true
+end
+
+function operators.noaccess()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if ta == 'string' or ta == 'file' or ta == 'array' then
+ if a[2] == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ a[2] = 'noaccess'
+ elseif ta == "dict" then
+ local thedict = get_VM(a[4])
+ if thedict.access == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ thedict.access = 'noaccess'
+ else
+ return ps_error('typecheck')
+ end
+ push_opstack(a)
+ return true
+end
+
+function operators.readonly()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if ta == 'string' or ta == 'file' or ta == 'array' then
+ local aa = a[2]
+ if aa == 'noaccess' or aa == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ a[2] = 'read-only'
+ elseif ta == 'dict' then
+ local thedict = get_VM(a[4])
+ local access = thedict.access
+ if access == 'noaccess' or access == 'execute-only' then
+ return ps_error('invalidaccess')
+ end
+ thedict.access = 'read-only'
+ else
+ return ps_error('typecheck')
+ end
+ push_opstack(a)
+ return true
+end
+
+function operators.rcheck()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ local aa
+ if ta == 'string' or ta == 'file' or ta == 'array' then
+ aa = a[2]
+ elseif ta == 'dict' then
+ aa = get_VM(a[4]).access
+ else
+ return ps_error('typecheck')
+ end
+ push_opstack { 'boolean', 'unlimited', 'literal', aa == 'unlimited' or aa == 'read-only' }
+ return true
+end
+
+function operators.wcheck()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ local aa
+ if ta == 'string' or ta == 'file' or ta == 'array' then
+ aa = a[2]
+ elseif ta == 'dict' then
+ local thedict = get_VM(a[4])
+ aa = thedict.access
+ else
+ return ps_error('typecheck')
+ end
+ push_opstack { 'boolean', 'unlimited', 'literal', aa == 'unlimited' }
+ return true
+end
+
+function operators.cvi()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if ta == 'string' then
+ push_opstack(a)
+ local ret, err = operators.token()
+ if not ret then
+ return ret, err
+ end
+ local b = pop_opstack()
+ if b[4] == false then
+ return ps_error('syntaxerror')
+ end
+ a = pop_opstack()
+ pop_opstack() -- get rid of the postmatch string remains
+ ta = a[1]
+ end
+ local aa = a[2]
+ if not (aa == 'unlimited' or aa == 'read-only') then
+ return ps_error('invalidaccess')
+ end
+ if ta == 'integer' then
+ push_opstack(a)
+ elseif ta == 'real' then
+ local va = a[4]
+ local c = va < 0 and -floor(-va) or floor(ava)
+ if abs(c) > MAX_INT then
+ return ps_error('rangecheck')
+ end
+ push_opstack { 'integer', 'unlimited', 'literal', c }
+ else
+ return ps_error('typecheck')
+ end
+ return true
+end
+
+function operators.cvn()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, aa = a[1], a[2]
+ local ta = a[1]
+ if ta ~= 'string' then
+ return ps_error('typecheck')
+ end
+ if aa == 'execute-only' or aa == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ push_opstack { 'name', aa, a[3], add_VM(get_VM(a[4])) }
+ return true
+end
+
+function operators.cvr()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if ta == 'string' then
+ push_opstack(a)
+ local ret, err = operators.token()
+ if not ret then
+ return ret, err
+ end
+ local b = pop_opstack()
+ if b[4] == false then
+ return ps_error('syntaxerror')
+ end
+ a = pop_opstack()
+ pop_opstack() -- get rid of the postmatch string remains
+ ta = a[1]
+ end
+ local aa = a[2]
+ if not (aa == 'unlimited' or aa == 'read-only') then
+ return ps_error('invalidaccess')
+ end
+ if ta == 'integer' then
+ push_opstack { 'real', 'unlimited', 'literal', a[4] }
+ elseif ta == 'real' then
+ push_opstack(a)
+ else
+ return ps_error('typecheck')
+ end
+ return true
+end
+
+do
+
+ local byte0 = byte('0')
+ local byteA = byte('A') - 10
+
+ function operators.cvrs()
+ local c = pop_opstack()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb, tc = a[1], b[1], c[1]
+ if not (ta == 'integer' or ta == 'real') then
+ return ps_error('typecheck')
+ end
+ if not tb == 'integer' then
+ return ps_error('typecheck')
+ end
+ if not tc == 'string' then
+ return ps_error('typecheck')
+ end
+ if not c[2] == 'unlimited' then
+ return ps_error('invalidaccess')
+ end
+ local va, vb, vc = a[4], b[4], c[4]
+ if (vb < 2 or vb > 36) then
+ return ps_error('rangecheck')
+ end
+ if ta == 'real' then
+ push_opstack(a)
+ local ret, err = operators.cvi()
+ if ret then
+ return ret, err
+ end
+ a = pop_opstack()
+ end
+ -- todo: use an lpeg
+ local decimal = va
+ local str = { }
+ local n = 0
+ while decimal > 0 do
+ local digit = decimal % vb
+ n = n + 1
+ str[n] = digit < 10 and char(digit+byte0) or char(digit+byteA)
+ decimal = floor(decimal/vb)
+ end
+ if n > c[6] then
+ return ps_error('rangecheck')
+ end
+ str = concat(reverse(str))
+ local thestring = get_VM(vc)
+ VM[va] = str .. sub(thestring,n+1,-1)
+ push_opstack { c[1], c[2], c[3], add_VM(repl), n, n }
+ return true
+ end
+
+end
+
+function operators.cvs()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not 4 then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb = a[1], b[1]
+ local ab = b[2]
+ if not tb == 'string' then
+ return ps_error('typecheck')
+ end
+ if not ab == 'unlimited' then
+ return ps_error('invalidaccess')
+ end
+ local va, vb = a[4], b[4]
+ if ta == 'real' then
+ if floor(va) == va then
+ va = tostring(va) .. '.0'
+ else
+ va = tostring(va)
+ end
+ elseif ta == 'integer' then
+ va = tostring(va)
+ elseif ta == 'string' or ta == 'name' then
+ va = get_VM(va)
+ elseif ta == 'operator' then
+ va = a[5]
+ elseif ta == 'boolean' then
+ va = tostring(va)
+ else
+ va = "--nostringval--"
+ end
+ local n = #va
+ if n > b[6] then
+ return ps_error('rangecheck')
+ end
+ local thestring = get_VM(vb)
+ VM[vb] = va .. sub(thestring,n+1,-1)
+ push_opstack { tb, ab, b[3], add_VM(va), n, n }
+ return true
+end
+
+-- File operators
+--
+-- +file +closefile +read +write +writestring +readhexstring +writehexstring +readline ^token
+-- +bytesavailable +flush +flushfile +resetfile +status +run +currentfile +print ^= ^stack
+-- +== ^pstack ^prompt +echo
+
+function operators.file()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if b[1] ~= 'string' then
+ return ps_error('typecheck')
+ end
+ if a[1] ~= 'string' then
+ return ps_error('typecheck')
+ end
+ local fmode = get_VM(b[4])
+ local fname = get_VM(a[4])
+ -- only accept (r), (w) and (a)
+ if fmode ~= "r" and fmode ~= "w" and fmode ~= "a" then
+ return ps_error('typecheck')
+ end
+ if fname == "%stdin" then
+ -- can only read from stdin
+ if fmode ~= "r" then
+ return ps_error('invalidfileaccess')
+ end
+ push_opstack { 'file', 'unlimited', 'literal', 0, 0, 0, fmode, io.stdin }
+ elseif fname == "%stdout" then
+ -- can't read from stdout i.e. can only append, in fact, but lets ignore that
+ if fmode == "r" then
+ return ps_error('invalidfileaccess')
+ end
+ push_opstack { 'file', 'unlimited', 'literal', 0, 0, 0, fmode, io.stdout }
+ elseif fname == "%stderr" then
+ -- cant read from stderr i.e. can only append, in fact, but lets ignore that
+ if fmode == "r" then
+ return ps_error('invalidfileaccess')
+ end
+ push_opstack { 'file', 'unlimited', 'literal', 0, 0, 0, fmode, io.stderr }
+ elseif fname == "%statementedit" or fname == "%lineedit"then
+ return ps_error('invalidfileaccess')
+ else
+ -- so it is a normal file
+ local myfile, error = io.open(fname,fmode)
+ if not myfile then
+ return ps_error('undefinedfilename')
+ end
+ if fmode == 'r' then
+ l = myfile:read("*a")
+ if not l then
+ return ps_error('invalidfileaccess')
+ end
+ -- myfile:close() -- do not close here, easier later on
+ push_opstack { 'file', 'unlimited', 'literal', add_VM(l), 1, #l, fmode, myfile}
+ else
+ push_opstack { 'file', 'unlimited', 'literal', 0, 0, 0, fmode, myfile}
+ end
+ end
+ return true
+end
+
+function operators.read()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'file' then
+ return ps_error('typecheck')
+ end
+ if a[7] ~= 'r' then
+ return ps_error('invalidaccess')
+ end
+ local b
+ local v = a[4]
+ local f = a[8]
+ if v > 0 then
+ local thestr = get_VM(v)
+ local n = a[5]
+ if n < a[6] then
+ byte = sub(thestr,n,n+1)
+ -- a[5] = n + 1
+ end
+ else -- %stdin
+ b = f:read(1)
+ end
+ if b then
+ push_opstack { 'integer', 'unlimited', 'literal', byte(b) }
+ push_opstack { 'boolean', 'unlimited', 'literal', true }
+ else
+ f:close()
+ push_opstack { 'boolean', 'unlimited', 'literal', false}
+ end
+ return true
+end
+
+function operators.write()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if b[1] ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ if a[1] ~= 'file' then
+ return ps_error('typecheck')
+ end
+ if a[7] == 'r' then
+ return ps_error('ioerror')
+ end
+ a[8]:write(char(b[4] % 256))
+ return true
+end
+
+function operators.writestring()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if b[1] ~= 'string' then
+ return ps_error('typecheck')
+ end
+ if a[1] ~= 'file' then
+ return ps_error('typecheck')
+ end
+ if a[7] == 'r' then
+ return ps_error('ioerror')
+ end
+ a[8]:write(get_VM(b[4]))
+ return true
+end
+
+function operators.writehexstring()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if b[1] ~= 'string' then
+ return ps_error('typecheck')
+ end
+ if a[1] ~= 'file' then
+ return ps_error('typecheck')
+ end
+ if a[7] == 'r' then
+ return ps_error('ioerror')
+ end
+ local f = a[8]
+ local s = get_VM(b[4])
+ for w in gmatch(s,".") do
+ f:write(format("%x",byte(w))) -- we have a table for that somewhere
+ end
+ return true
+end
+
+do
+
+ local function get_string_line(a)
+ local str = get_VM(a[4])
+ local start = a[5]
+ local theend = a[6]
+ if start == theend then
+ return nil
+ end
+ str = match(str,"[\n\r]*([^\n\r]*)",start)
+ a[5] = a[5] + #str + 1 -- ?
+ return str
+ end
+
+ local function get_hexstring_line (a,b)
+ local thestring = get_VM(a[4])
+ local start, theend = a[5], a[6]
+ if start == theend then
+ return nil
+ end
+ local prefix, result, n = nil, { }, 0
+ local nmax = b[6]
+ while start < theend do
+ local b = sub(thestring,start,start)
+ if not b then
+ break
+ end
+ local hexbyte = tonumber(b,16)
+ if not hexbyte then
+ -- skip
+ elseif prefix then
+ n = n + 1
+ result[n] = char(prefix*16+hexbyte)
+ if n == nmax then
+ break
+ else
+ prefix = nil
+ end
+ else
+ prefix = hexbyte
+ end
+ start = start + 1
+ end
+ a[5] = start + 1 -- ?
+ return concat(result)
+ end
+
+ function operators.readline()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'file' then
+ return ps_error('typecheck')
+ end
+ if a[7] ~= 'r' then
+ return ps_error('invalidaccess')
+ end
+ local va = a[4]
+ if va > 0 then
+ va = get_string_line(a)
+ else
+ va = a[8]:read('*l')
+ end
+ if not va then
+ push_opstack { 'string', 'unlimited', 'literal', add_VM(''), 0, 0 }
+ push_opstack { 'boolean', 'unlimited', 'literal', false }
+ else
+ local n = #va
+ if n > b[6] then
+ return ps_error('rangecheck')
+ end
+ local thestring = get_VM(b[4])
+ VM[b[4]] = va .. sub(thestring,#va+1, -1)
+ push_opstack { 'string', 'unlimited', 'literal', add_VM(va), n, n }
+ push_opstack { 'boolean', 'unlimited', 'literal', true }
+ end
+ return true
+ end
+
+ function operators.readhexstring()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if not (ta == 'string' or ta == 'file') then
+ return ps_error('typecheck')
+ end
+ local thefile = a[8]
+ local va = a[4]
+ if va > 0 then
+ va = get_hexstring_line (a,b)
+ else
+ local prefix, result, n = nil, { }, 0
+ -- todo: read #va bytes and lpeg
+ while true do
+ local b = thefile:read(1)
+ if not b then
+ break
+ end
+ local hexbyte = tonumber(b,16)
+ local nmax = b[6]
+ if not hexbyte then
+ -- skip
+ elseif prefix then
+ n = n + 1
+ result[n] = char(prefix*16+hexbyte)
+ if n == nmax then
+ break
+ else
+ prefix = nil
+ end
+ else
+ prefix = hexbyte
+ end
+ end
+ va = concat(result)
+ end
+ local thestring = get_VM(b[4])
+ local n = #va
+ VM[b[4]] = repl .. sub(thestring,n+1,-1)
+ push_opstack { b[1], b[2], b[3], add_VM(va), n, n }
+ push_opstack { 'boolean', 'unlimited', 'literal', n == b[6] }
+ return true
+ end
+
+end
+
+function operators.flush()
+ io.flush()
+ return true
+end
+
+function operators.bytesavailable()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'file' then
+ return ps_error('typecheck')
+ end
+ if a[7] ~= 'r' then
+ return ps_error('typecheck')
+ end
+ local waiting = (a[4] > 0) and (a[6] - a[5] + 1) or -1
+ push_opstack { "integer", "unlimited", "literal", waiting }
+ return true
+end
+
+-- this does not really do anything useful
+
+function operators.resetfile()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'file' then
+ return ps_error('typecheck')
+ end
+ return true
+end
+
+function operators.flushfile()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'file' then
+ return ps_error('typecheck')
+ end
+ if a[4] > 0 then
+ a[5] = a[6]
+ else
+ a[8]:flush()
+ end
+ return true
+end
+
+function operators.closefile()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'file' then
+ return ps_error('typecheck')
+ end
+ if a[7] == 'r' then
+ a[5] = a[6]
+ else
+ push_opstack(a)
+ operators.flushfile()
+ end
+ a[8]:close()
+ return true
+end
+
+function operators.status()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'file' then
+ return ps_error('typecheck')
+ end
+ local state = io.type(a[8])
+ push_opstack { "boolean", 'unlimited', 'literal', not state or state == "closed file" }
+ return true
+end
+
+function operators.run()
+ push_opstack { "string", "unlimited", "literal", add_VM("r"), 1, 1 }
+ local ret, err = operators.file()
+ if not ret then
+ return ret, err
+ end
+ ret, err = operators.cvx()
+ if not ret then
+ return ret, err
+ end
+ local a = pop_opstack() -- an executable file
+ push_execstack { ".run", "unlimited", "literal", false } -- constant
+ local curstack = execstackptr
+ local thefile = a[8]
+ push_execstack(a)
+ while curstack < execstackptr do
+ do_exec()
+ end
+ local state = io.type(thefile)
+ if not state or state == "closed file" then
+ -- okay
+ else
+ thefile:close()
+ end
+ if execstackptr > 0 then
+ local entry = execstack[execstackptr]
+ if entry[1] == '.run' and entry[4] == true then
+ pop_execstack()
+ end
+ end
+ return true
+end
+
+function operators.currentfile()
+ local n = execstackptr
+ while n >= 0 do
+ local entry = execstack[n]
+ if entry[1] == 'file' and entry[7] == 'r' then
+ push_opstack(entry)
+ return true
+ end
+ n = n - 1
+ end
+ push_opstack { 'file', 'unlimited', 'executable', add_VM(''), 0, 0, 'r', stdin }
+ return true
+end
+
+function operators.print()
+ local a = pop_opstack()
+ if not a then return
+ ps_error('stackunderflow')
+ end
+ if a[1] ~= 'string' then
+ return ps_error('typecheck')
+ end
+ report(get_VM(a[4]))
+end
+
+-- '=' is also defined as a procedure below;
+--
+-- it is actually supposed to do this: "equaldict begin dup type exec end"
+-- where each of the entries in equaldict handles one type only, but this
+-- works just as well
+
+do
+
+ local pattern = Cs(
+ Cc("(")
+ * (
+ P("\n") / "\\n"
+ + P("\r") / "\\r"
+ + P("(") / "\\("
+ + P(")") / "\\)"
+ + P("\\") / "\\\\"
+ + P("\b") / "\\b"
+ + P("\t") / "\\t"
+ + P("\f") / "\\f"
+ + R("\000\032","\127\255") / tonumber / formatters["\\%03o"]
+ + P(1)
+ )^0
+ * Cc(")")
+ )
+
+ -- print(lpegmatch(pattern,[[h(a\nn)s]]))
+
+ local function do_operator_equal(a)
+ local ta, va = a[1], a[4]
+ if ta == 'real' then
+ if floor(va) == va then
+ return tostring(va .. '.0')
+ else
+ return tostring(va)
+ end
+ elseif ta == 'integer' then
+ return tostring(va)
+ elseif ta == 'string' then
+ return lpegmatch(pattern,get_VM(va))
+ elseif ta == 'boolean' then
+ return tostring(va)
+ elseif ta == 'operator' then
+ return '--' .. a[5] .. '--'
+ elseif ta == 'name' then
+ if a[3] == 'literal' then
+ return '/' .. get_VM(va)
+ else
+ return get_VM(va)
+ end
+ elseif ta == 'array' then
+ va = get_VM(va)
+ local isexec = a[3] == 'executable'
+ local result = { isexec and "{" or "[" }
+ local n = 1
+ for i=1,#va do
+ n = n + 1
+ result[n] = do_operator_equal(va[i])
+ end
+ result[n+1] = isexec and "}" or "]"
+ return concat(result," ")
+ elseif ta == 'null' then
+ return 'null'
+ elseif ta == 'dict' then
+ return '-dicttype-'
+ elseif ta == 'save' then
+ return '-savetype-'
+ elseif ta == 'mark' then
+ return '-marktype-'
+ elseif ta == 'file' then
+ return '-filetype-'
+ elseif ta == 'font' then
+ return '-fonttype-'
+ end
+ end
+
+ function operators.equal()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ report(do_operator_equal(a))
+ return true
+ end
+
+end
+
+local function commonstack(seperator)
+ for n=1,opstackptr do
+ push_opstack { 'string', 'unlimited', 'literal', add_VM(seperator), 1 ,1 }
+ push_opstack(opstack[n])
+ push_execstack { 'operator','unlimited','executable', operators.print, 'print'}
+ push_execstack { 'operator','unlimited','executable', operators.equal, '=='}
+ end
+ return true
+end
+
+function operators.pstack()
+ return commonstack("\n")
+end
+
+function operators.stack()
+ return commonstack(" ")
+end
+
+-- this does not really do anything useful
+
+function operators.echo()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'boolean' then
+ return ps_error('typecheck')
+ end
+ return true
+end
+
+-- Virtual memory operators
+--
+-- +save +restore +vmstatus
+
+-- to be checked: we do a one-level shallow copy now, not sure if that
+-- is good enough yet
+
+local savelevel = 0
+
+initializers[#initializers+1] = function(reset)
+ savelevel = 0
+end
+
+function operators.save()
+ local saved_VM = { }
+ for k1, v1 in next, VM do
+ if type(v1) == "table" then
+ local t1 = { }
+ saved_VM[k1] = t1
+ for k2, v2 in next, t1 do
+ if type(v2) == "table" then
+ local t2 = { }
+ t1[k2] = t2
+ for k3, v3 in next, v2 do
+ t2[k3] = v3
+ end
+ else
+ t1[k2] = v2
+ end
+ end
+ else
+ saved_VM[k1] = v1
+ end
+ end
+ push_gsstack { 'save', copy_gsstate() }
+ savelevel = savelevel + 1
+ push_opstack { 'save', 'unlimited', 'executable', add_VM(saved_VM) }
+end
+
+do
+
+ local function validstack(stack,index,saved_VM)
+ -- loop over pstack, execstack, and dictstack to make sure
+ -- there are no entries with VM_id > #saved_VM
+ for i=index,1,-1 do
+ local v = stack[i]
+ if type(v) == "table" then
+ local tv = v[1]
+ if tv == "save" or tv == "string" or tv == "array" or tv == "dict" or tv == "name" or tv == "file" then
+ -- todo: check on %stdin/%stdout, but should be ok
+ if v[4] > #saved_VM then
+ return false
+ end
+ end
+ end
+ i = i - 1
+ end
+ return true
+ end
+
+ function operators.restore()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'save' then
+ return ps_error('typecheck')
+ end
+ if a[4] == 0 or savelevel == 0 then
+ return ps_error('invalidrestore')
+ end
+ local saved_VM = get_VM(a[4])
+ if directvm then
+ else
+ if not validstack(execstack,execstackptr,saved_VM) then
+ return ps_error('invalidrestore')
+ end
+ if not validstack(dictstack,dictstackptr,saved_VM) then
+ return ps_error('invalidrestore')
+ end
+ if not validstack(opstack,opstackptr,saved_VM) then
+ return ps_error('invalidrestore')
+ end
+ end
+ while gsstackptr > 0 do
+ local g = gsstack[gsstackptr]
+ gsstackptr = gsstackptr - 1
+ if g[1] == "save" then
+ gsstate = g[2]
+ return
+ end
+ end
+ a[4] = 0 -- invalidate save object
+ savelevel = savelevel - 1
+ VM = saved_VM
+ end
+
+end
+
+function operators.vmstatus()
+ local n = 0 -- #VM * 100
+ push_opstack { 'integer', 'unlimited', 'literal', savelevel }
+ push_opstack { 'integer', 'unlimited', 'literal', n }
+ push_opstack { 'integer', 'unlimited', 'literal', n }
+ return true
+end
+
+-- Miscellaneous operators
+--
+-- +bind +null +usertime +version
+
+-- the reference manual says bind only ERRORS on typecheck
+
+local function bind()
+ local a = pop_opstack()
+ if not a then
+ return true -- ps_error('stackunderflow')
+ end
+ if not a[1] == 'array' then
+ return ps_error('typecheck')
+ end
+ local proc = get_VM(a[4])
+ for i=1,#proc do
+ local v = proc[i]
+ local t = v[1]
+ if t == 'name' then
+ if v[3] == 'executable' then
+ local op = lookup(get_VM(v[4]))
+ if op and op[1] == 'operator' then
+ proc[i] = op
+ end
+ end
+ elseif t == 'array' then
+ if v[2] == 'unlimited' then
+ push_opstack(v)
+ bind() -- recurse
+ pop_opstack()
+ proc[i][2] = 'read-only'
+ end
+ end
+ end
+ push_opstack(a)
+end
+
+operators.bind = bind
+
+function operators.null()
+ push_opstack { 'null', 'unlimited', 'literal' }
+ return true
+end
+
+function operators.usertime()
+ push_opstack { 'integer', 'unlimited', 'literal', floor(os.clock() * 1000) }
+ return true
+end
+
+function operators.version()
+ push_opstack { 'string', 'unlimited', 'literal', add_VM('23.0') }
+ return true
+end
+
+-- Graphics state operators
+--
+-- +gsave +grestore +grestoreall +initgraphics +setlinewidth +currentlinewidth +setlinecap +currentlinecap
+-- +setlinejoin +currentlinejoin +setmiterlimit +currentmiterlimit +setdash +currentdash +setflat +currentflat
+-- +setgray +currentgray +sethsbcolor +currenthsbcolor +setrgbcolor +setcmykcolor +currentrgbcolor +setscreen
+-- +currentscreen +settransfer +currenttransfer
+
+function operators.gsave()
+ push_gsstack { 'gsave', copy_gsstate() }
+ return true
+end
+
+function operators.grestore()
+ if gsstackptr > 0 then
+ local g = gsstack[gsstackptr]
+ if g[1] == "gsave" then
+ gsstackptr = gsstackptr - 1
+ gsstate = g[2]
+ end
+ end
+ return true
+end
+
+function operators.grestoreall() -- needs checking
+ for i=gsstackptr,1,-1 do
+ local g = gsstack[i]
+ if g[1] == "save" then
+ gsstate = g[2]
+ gsstackptr = i
+ return true
+ end
+ end
+ gsstackptr = 0
+ return true
+end
+
+function operators.initgraphics()
+ local newstate = copy_gsstate() -- hm
+ newstate.matrix = { 1, 0, 0, 1, 0, 0 }
+ newstate.color = { gray = 0, hsb = { }, rgb = { }, cmyk = { }, type = "gray" }
+ newstate.position = { } -- actual x and y undefined
+ newstate.path = { }
+ newstate.linewidth = 1
+ newstate.linecap = 0
+ newstate.linejoin = 0
+ newstate.miterlimit = 10
+ newstate.dashpattern = { }
+ newstate.dashoffset = 0
+ gsstate = newstate
+ device.initgraphics()
+ operators.initclip()
+ return true
+end
+
+function operators.setlinewidth()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local t = a[1]
+ if not (t == 'integer' or t == 'real') then
+ return ps_error('typecheck')
+ end
+ gsstate.linewidth = a[4]
+ return true
+end
+
+function operators.currentlinewidth()
+ local w = gsstate.linewidth
+ push_opstack {
+ (abs(w) > MAX_INT or floor(w) ~= w) and 'real' or 'integer',
+ 'unlimited',
+ 'literal',
+ w,
+ }
+ return true
+end
+
+function operators.setlinecap()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ local c = a[4]
+ if c > 2 or c < 0 then
+ return ps_error('rangecheck')
+ end
+ gsstate.linecap = c
+ return true
+end
+
+function operators.currentlinecap()
+ push_opstack { 'integer', 'unlimited', 'literal', gsstate.linecap }
+ return true
+end
+
+function operators.setlinejoin()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'integer' then
+ return ps_error('typecheck')
+ end
+ local j = a[4]
+ if j > 2 or j < 0 then
+ return ps_error('rangecheck')
+ end
+ gsstate.linejoin = j
+ return true
+end
+
+function operators.currentlinejoin()
+ push_opstack { 'integer', 'unlimited', 'literal', gsstate.linejoin }
+ return true
+end
+
+function operators.setmiterlimit()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local t = a[1]
+ if not (t == 'integer' or t == 'real') then
+ return ps_error('typecheck')
+ end
+ local m = a[4]
+ if m < 1 then
+ return ps_error('rangecheck')
+ end
+ gsstate.miterlimit = m
+ return true
+end
+
+function operators.currentmiterlimit()
+ local w = gsstate.miterlimit
+ push_opstack {
+ (abs(w) > MAX_INT or floor(w) ~= w) and 'real' or 'integer',
+ 'unlimited',
+ 'literal',
+ w
+ }
+ return true
+end
+
+function operators.setdash()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb = a[1], b[1]
+ if ta ~= 'array' then
+ return ps_error('typecheck')
+ end
+ if not (tb == 'integer' or tb == 'real') then
+ return ps_error('typecheck')
+ end
+ local pattern = { }
+ local total = 0
+ local thearray = get_VM(a[4])
+ for i=1,#thearray do
+ local a = thearray[i]
+ local ta, va = a[1], a[4]
+ if ta ~= "integer" then
+ return ps_error('typecheck')
+ end
+ if va < 0 then
+ return ps_error('limitcheck')
+ end
+ total = total + va
+ pattern[#pattern+1] = va
+ end
+ if #pattern > 0 and total == 0 then
+ return ps_error('limitcheck')
+ end
+ gsstate.dashpattern = pattern
+ gsstate.dashoffset = b[4]
+ return true
+end
+
+function operators.currentdash()
+ local thearray = gsstate.dashpattern
+ local pattern = { }
+ for i=1,#thearray do
+ pattern[i] = { 'integer', 'unlimited', 'literal', thearray[i] }
+ end
+ push_opstack { 'array', 'unlimited', 'literal', add_VM(pattern), #pattern, #pattern }
+ local w = gsstate.dashoffset
+ push_opstack {
+ (abs(w) > MAX_INT or floor(w) ~= w) and 'real' or 'integer', 'unlimited', 'literal', w
+ }
+ return true
+end
+
+function operators.setflat()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, va = a[1], a[4]
+ if not (ta == 'integer' or ta == 'real') then
+ return ps_error('typecheck')
+ end
+ gsstate.flatness = va
+ return true
+end
+
+function operators.currentflat()
+ local w = gsstate.flatness
+ push_opstack {
+ (abs(w) > MAX_INT or floor(w) ~= w) and 'real' or 'integer', 'unlimited', 'literal', w
+ }
+ return true
+end
+
+-- Color conversion functions
+--
+-- normally, level one colors are based on hsb, but for our backend it is better to
+-- stick with the original request when possible
+
+do
+
+ local function rgb_to_gray (r, g, b)
+ return 0.30 * r + 0.59 * g + 0.11 * b
+ end
+
+ local function cmyk_to_gray (c, m, y, k)
+ return 0.30 * (1.0 - min(1.0,c+k)) + 0.59 * (1.0 - min(1.0,m+k)) + 0.11 * (1.0 - min(1.0,y+k))
+ end
+
+ local function cmyk_to_rgb (c, m, y, k)
+ return 1.0 - min(1.0,c+k), 1.0 - min(1.0,m+k), 1.0 - min(1.0,y+k)
+ end
+
+ local function rgb_to_hsv(r, g, b)
+ local offset, maximum, other_1, other_2
+ if r >= g and r >= b then
+ offset, maximum, other_1, other_2 = 0, r, g, b
+ elseif g >= r and g >= b then
+ offset, maximum, other_1, other_2 = 2, g, b, r
+ else
+ offset, maximum, other_1, other_2 = 4, b, r, g
+ end
+ if maximum == 0 then
+ return 0, 0, 0
+ end
+ local minimum = other_1 < other_2 and other_1 or other_2
+ if maximum == minimum then
+ return 0, 0, maximum
+ end
+ local delta = maximum - minimum
+ return (offset + (other_1-other_2)/delta)/6, delta/maximum, maximum
+ end
+
+ local function gray_to_hsv (col)
+ return 0, 0, col
+ end
+
+ local function gray_to_rgb (col)
+ return 1-col, 1-col, 1-col
+ end
+
+ local function gray_to_cmyk (col)
+ return 0, 0, 0, col
+ end
+
+ local function hsv_to_rgb(h,s,v)
+ local hi = floor(h * 6.0) % 6
+ local f = (h * 6) - floor(h * 6)
+ local p = v * (1 - s)
+ local q = v * (1 - f * s)
+ local t = v * (1 - (1 - f) * s)
+ if hi == 0 then
+ return v, t, p
+ elseif hi == 1 then
+ return q, v, p
+ elseif hi == 2 then
+ return p, v, t
+ elseif hi == 3 then
+ return p, q, v
+ elseif hi == 4 then
+ return t, p, v
+ elseif hi == 5 then
+ return v, p, q
+ end
+ end
+
+ local function hsv_to_gray(h,s,v)
+ return rgb_to_gray(hsv_to_rgb(h,s,v))
+ end
+
+ -- color operators
+
+ function operators.setgray()
+ local g = pop_opstack()
+ if not g then
+ return ps_error('stackunderflow')
+ end
+ local gt = g[1]
+ if not (gt == 'integer' or gt == 'real') then
+ return ps_error('typecheck')
+ end
+ local gv = g[4]
+ local color = gsstate.color
+ color.type = "gray"
+ color.gray = (gv < 0 and 0) or (gv > 1 and 1) or gv
+ return true
+ end
+
+ function operators.currentgray()
+ local color = gsstate.color
+ local t = color.type
+ local s
+ if t == "gray" then
+ s = color.gray
+ elseif t == "rgb" then
+ local col = color.rgb
+ s = rgb_to_gray(col[1],col[2],col[3])
+ elseif t == "cmyk" then
+ local col = cmyk
+ s = cmyk_to_gray(col[1],col[2],col[3],col[4])
+ else
+ local col = color.hsb
+ s = hsv_to_gray(col[1],col[2],col[3])
+ end
+ push_opstack { (s == 0 or s == 1) and 'integer' or 'real', 'unlimited', 'literal', s }
+ return true
+ end
+
+ function operators.sethsbcolor()
+ local b = pop_opstack()
+ local s = pop_opstack()
+ local h = pop_opstack()
+ if not h then
+ return ps_error('stackunderflow')
+ end
+ local ht, st, bt = h[1], s[1], b[1]
+ if not (ht == 'integer' or ht == 'real') then
+ return ps_error('typecheck')
+ end
+ if not (st == 'integer' or st == 'real') then
+ return ps_error('typecheck')
+ end
+ if not (bt == 'integer' or bt == 'real') then
+ return ps_error('typecheck')
+ end
+ local hv, sv, bv = h[4], s[4], b[4]
+ local color = gsstate.color
+ color.type = "hsb"
+ color.hsb = {
+ (hv < 0 and 0) or (hv > 1 and 1) or hv,
+ (sv < 0 and 0) or (sv > 1 and 1) or sv,
+ (bv < 0 and 0) or (bv > 1 and 1) or bv,
+ }
+ return true
+ end
+
+ function operators.currenthsbcolor()
+ local color = gsstate.color
+ local t = color.type
+ local h, s, b
+ if t == "gray" then
+ h, s, b = gray_to_hsv(color.gray)
+ elseif t == "rgb" then
+ local col = color.rgb
+ h, s, b = rgb_to_hsv(col[1],col[2],col[3])
+ elseif t == "cmyk" then
+ local col = color.cmyk
+ h, s, b = cmyk_to_hsv(col[1],col[2],col[3],col[4])
+ else
+ local col = color.hsb
+ h, s, b = col[1], col[2], col[3]
+ end
+ push_opstack { (h == 0 or h == 1) and 'integer' or 'real', 'unlimited', 'literal', h }
+ push_opstack { (s == 0 or s == 1) and 'integer' or 'real', 'unlimited', 'literal', s }
+ push_opstack { (b == 0 or b == 1) and 'integer' or 'real', 'unlimited', 'literal', b }
+ return true
+ end
+
+ function operators.setrgbcolor()
+ local b = pop_opstack()
+ local g = pop_opstack()
+ local r = pop_opstack()
+ if not r then
+ return ps_error('stackunderflow')
+ end
+ local rt, gt, bt = r[1], g[1], b[1]
+ if not (rt == 'integer' or rt == 'real') then
+ return ps_error('typecheck')
+ end
+ if not (gt == 'integer' or gt == 'real') then
+ return ps_error('typecheck')
+ end
+ if not (bt == 'integer' or bt == 'real') then
+ return ps_error('typecheck')
+ end
+ local rv, gv, bv = r[4], g[4], b[4]
+ local color = gsstate.color
+ color.type = "rgb"
+ color.rgb = {
+ (rv < 0 and 0) or (rv > 1 and 1) or rv,
+ (gv < 0 and 0) or (gv > 1 and 1) or gv,
+ (bv < 0 and 0) or (bv > 1 and 1) or bv,
+ }
+ return true
+ end
+
+ function operators.currentrgbcolor()
+ local color = gsstate.color
+ local t = color.type
+ local r, g, b
+ if t == "gray" then
+ r, g, b = gray_to_rgb(color.gray)
+ elseif t == "rgb" then
+ local col = color.rgb
+ r, g, b = col[1], col[2], col[3]
+ elseif t == "cmyk" then
+ r, g, b = cmyk_to_rgb(color.cmyk)
+ else
+ local col = color.hsb
+ r, g, b = hsv_to_rgb(col[1], col[2], col[3])
+ end
+ push_opstack { (r == 0 or r == 1) and "integer" or "real", 'unlimited', 'literal', r }
+ push_opstack { (g == 0 or g == 1) and "integer" or "real", 'unlimited', 'literal', g }
+ push_opstack { (b == 0 or b == 1) and "integer" or "real", 'unlimited', 'literal', b }
+ return true
+ end
+
+ function operators.setcmykcolor()
+ local k = pop_opstack()
+ local y = pop_opstack()
+ local m = pop_opstack()
+ local c = pop_opstack()
+ if not c then
+ return ps_error('stackunderflow')
+ end
+ local ct, mt, yt, kt = c[1], m[1], y[1], k[1]
+ if not (ct == 'integer' or ct == 'real') then
+ return ps_error('typecheck')
+ end
+ if not (mt == 'integer' or mt == 'real') then
+ return ps_error('typecheck')
+ end
+ if not (yt == 'integer' or yt == 'real') then
+ return ps_error('typecheck')
+ end
+ if not (kt == 'integer' or kt == 'real') then
+ return ps_error('typecheck')
+ end
+ local cv, mv, yv, kv = c[4], m[4], y[4], k[4]
+ local color = gsstate.color
+ color.type = "cmyk"
+ color.cmyk = {
+ (cv < 0 and 0) or (cv > 1 and 1) or cv,
+ (mv < 0 and 0) or (mv > 1 and 1) or mv,
+ (yv < 0 and 0) or (yv > 1 and 1) or yv,
+ (kv < 0 and 0) or (kv > 1 and 1) or kv,
+ }
+ return true
+ end
+
+ function operators.currentcmykcolor()
+ local color = gsstate.color
+ local t = color.type
+ local c, m, y, k
+ if t == "gray" then
+ c, m, y, k = gray_to_cmyk(color.gray)
+ elseif t == "rgb" then
+ c, m, y, k = rgb_to_cmyk(color.rgb)
+ elseif t == "cmyk" then
+ local col = color.cmyk
+ c, m, y, k = col[1], col[2], col[3], col[4]
+ else
+ local col = color.hsb
+ c, m, y, k = hsv_to_cmyk(col[1], col[2], col[3])
+ end
+ push_opstack { (c == 0 or c == 1) and "integer" or "real", 'unlimited', 'literal', c }
+ push_opstack { (m == 0 or m == 1) and "integer" or "real", 'unlimited', 'literal', m }
+ push_opstack { (y == 0 or y == 1) and "integer" or "real", 'unlimited', 'literal', y }
+ push_opstack { (k == 0 or k == 1) and "integer" or "real", 'unlimited', 'literal', k }
+ return true
+ end
+
+end
+
+function operators.setscreen()
+ local c = pop_opstack()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb, tc, ac = a[1], b[1], c[1], c[3]
+ if not (tc == 'array' and ac == 'executable') then
+ return ps_error('typecheck')
+ end
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ local va, vb, vc = a[4], b[4], c[4]
+ if vb < 0 or vb > 360 then
+ return ps_error('rangecheck')
+ end
+ if va < 0 then
+ return ps_error('rangecheck')
+ end
+ gsstate.screen = { va, vb, vc }
+ return true
+end
+
+function operators.currentscreen()
+ local w
+ if not gsstate.screen then
+ local popper = { 'operator', 'unlimited', 'executable', operators.pop, 'pop' }
+ push_opstack { 'integer', 'unlimited', 'literal', 1 }
+ push_opstack { 'integer', 'unlimited', 'literal', 0 }
+ push_opstack { 'array', 'unlimited', 'executable', add_VM{ popper }, 1, 1, 'd' }
+ else
+ local w1 = gsstate.screen[1]
+ local w2 = gsstate.screen[2]
+ local w3 = gsstate.screen[3]
+ push_opstack {
+ (abs(w) > MAX_INT or floor(w1) ~= w1) and 'real' or 'integer', 'unlimited', 'literal', w1
+ }
+ push_opstack {
+ (abs(w) > MAX_INT or floor(w2) ~= w2) and 'real' or 'integer', 'unlimited', 'literal', w2
+ }
+ local thearray = get_VM(w3)
+ push_opstack { 'array', 'unlimited', 'executable', w3, 1, #thearray, 'd' } -- w3 or thearray ?
+ end
+ return true
+end
+
+function operators.settransfer()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if not (a[1] == 'array' and a[3] == 'executable') then
+ return ps_error('typecheck')
+ end
+ local va = a[4]
+ if va < 0 then
+ return ps_error('rangecheck')
+ end
+ gsstate.transfer = va
+ return true
+end
+
+function operators.currenttransfer()
+ local transfer = gsstate.transfer
+ if not transfer then
+ push_opstack { 'array', 'unlimited', 'executable', add_VM{ }, 0, 0, 'd'}
+ else
+ local thearray = get_VM(transfer)
+ push_opstack { 'array', 'unlimited', 'executable', transfer, 1, #thearray, 'd' }
+ end
+ return true
+end
+
+-- Coordinate system and matrix operators
+--
+-- +matrix +initmatrix +identmatrix +defaultmatrix +currentmatrix +setmatrix +translate
+-- +scale +rotate +concat +concatmatrix +transform +dtransform +itransform +idtransform
+-- +invertmatrix
+
+-- are these changed in place or not? if not then we can share
+
+function operators.matrix()
+ local matrix = {
+ {'real', 'unlimited', 'literal', 1},
+ {'real', 'unlimited', 'literal', 0},
+ {'real', 'unlimited', 'literal', 0},
+ {'real', 'unlimited', 'literal', 1},
+ {'real', 'unlimited', 'literal', 0},
+ {'real', 'unlimited', 'literal', 0},
+ }
+ push_opstack { 'array', 'unlimited', 'literal', add_VM(matrix), 6, 6 }
+ return true
+end
+
+function operators.initmatrix()
+ gsstate.matrix = { 1, 0, 0, 1, 0, 0 }
+ return true
+end
+
+function operators.identmatrix()
+ local a = pop_opstack()
+ if not a then return
+ ps_error('stackunderflow')
+ end
+ if a[1] ~= 'array' then
+ return ps_error('typecheck')
+ end
+ if a[6] < 6 then
+ return ps_error('rangecheck')
+ end
+ local m = VM[a[4]] -- or can we replace the numbers
+ m[1] = { 'real', 'unlimited', 'literal', 1 }
+ m[2] = { 'real', 'unlimited', 'literal', 0 }
+ m[3] = { 'real', 'unlimited', 'literal', 0 }
+ m[4] = { 'real', 'unlimited', 'literal', 1 }
+ m[5] = { 'real', 'unlimited', 'literal', 0 }
+ m[6] = { 'real', 'unlimited', 'literal', 0 }
+ a[5] = 6
+ push_opstack(a)
+ return true
+end
+
+operators.defaultmatrix = operators.identmatrix
+
+function operators.currentmatrix()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'array' then
+ return ps_error('typecheck')
+ end
+ if a[6] < 6 then
+ return ps_error('rangecheck')
+ end
+ local thearray = get_VM(a[4])
+ local matrix = gsstate.matrix
+ for i=1,6 do
+ thearray[i] = {'real', 'unlimited', 'literal', matrix[i]}
+ end
+ push_opstack { 'array', 'unlimited', 'literal', a[4], 6, 6 }
+ return true
+end
+
+function operators.setmatrix()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'array' then
+ return ps_error('typecheck')
+ end
+ if a[6] ~= 6 then
+ return ps_error('rangecheck')
+ end
+ local thearray = get_VM(a[4])
+ local matrix = gsstate.matrix
+ for i=1,#thearray do
+ local a = thearray[i]
+ local ta, tv = a[1], a[4]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ if i > 6 then
+ return ps_error('rangecheck')
+ end
+ matrix[i] = va
+ end
+ return true
+end
+
+local function do_transform(matrix,a,b)
+ local x = matrix[1] * a + matrix[3] * b + matrix[5]
+ local y = matrix[2] * a + matrix[4] * b + matrix[6]
+ return x, y
+end
+
+local function do_itransform(matrix,a,b)
+ local m1 = matrix[1]
+ local m4 = matrix[4]
+ if m1 == 0 or m4 == 0 then
+ return nil
+ end
+ local x = (a - matrix[5] - matrix[3] * b) / m1
+ local y = (b - matrix[6] - matrix[2] * a) / m4
+ return x, y
+end
+
+local function do_concat (a,b)
+ local a1, a2, a3, a4, a5, a6 = a[1], a[2], a[3], a[4], a[5], a[6]
+ local b1, b2, b3, b4, b5, b6 = b[1], b[2], b[3], b[4], b[5], b[6]
+ local c1 = a1 * b1 + a2 * b3
+ local c2 = a1 * b2 + a2 * b4
+ local c3 = a1 * b3 + a3 * b4
+ local c4 = a3 * b2 + a4 * b4
+ local c5 = a5 * b1 + a6 * b3 + b5
+ local c6 = a5 * b2 + a6 * b4 + b6
+ -- this is because double calculation introduces a small error
+ return {
+ abs(c1) < 1.0e-16 and 0 or c1,
+ abs(c2) < 1.0e-16 and 0 or c2,
+ abs(c3) < 1.0e-16 and 0 or c3,
+ abs(c4) < 1.0e-16 and 0 or c4,
+ abs(c5) < 1.0e-16 and 0 or c5,
+ abs(c6) < 1.0e-16 and 0 or c6,
+ }
+end
+
+local function do_inverse (a)
+ local a1, a2, a3, a4, a5, a6 = a[1], a[2], a[3], a[4], a[5], a[6]
+ local det = a1 * a4 - a3 * a2
+ if det == 0 then
+ return nil
+ end
+ local c1 = a4 / det
+ local c3 = -a3 / det
+ local c2 = -a2 / det
+ local c4 = a1 / det
+ local c5 = (a3 * a6 - a5 * a4) / det
+ local c6 = (a5 * a2 - a1 * a6) / det
+ return {
+ abs(c1) < 1.0e-16 and 0 or c1,
+ abs(c2) < 1.0e-16 and 0 or c2,
+ abs(c3) < 1.0e-16 and 0 or c3,
+ abs(c4) < 1.0e-16 and 0 or c4,
+ abs(c5) < 1.0e-16 and 0 or c5,
+ abs(c6) < 1.0e-16 and 0 or c6,
+ }
+end
+
+function operators.translate()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] == 'array' then
+ if a[6] ~= 6 then
+ return ps_error('typecheck')
+ end
+ local tf = a
+ local a = pop_opstack()
+ local b = pop_opstack()
+ if not b then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb = a[1], b[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ local m = VM[tf[4]]
+ local old = { m[1][4], m[2][4], m[3][4], m[4][4], m[5][4], m[6][4] }
+ local c = do_concat(old,{1,0,0,1,b[4],a[4]})
+ for i=1,6 do
+ m[i] = { 'real', 'unlimited', 'literal', c[i] }
+ end
+ tf[5] = 6
+ push_opstack(tf)
+ else
+ local b = pop_opstack()
+ local ta = a[1]
+ local tb = b[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ gsstate.matrix = do_concat(gsstate.matrix,{1,0,0,1,b[4],a[4]})
+ end
+ return true
+end
+
+function operators.scale()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if ta == 'array' then
+ local tf = a
+ if a[6] ~= 6 then
+ return ps_error('typecheck')
+ end
+ local a = pop_opstack()
+ local b = pop_opstack()
+ if not b then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb = a[1], b[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ local v = VM[tf[4]]
+ local c = do_concat (
+ { v[1][4], v[2][4], v[3][4], v[4][4], v[5][4], v[6][4] },
+ { b[4], 0, 0, a[4], 0, 0 }
+ )
+ for i=1,6 do
+ v[i] = { 'real', 'unlimited', 'literal', c[i] }
+ end
+ tf[5] = 6
+ push_opstack(tf)
+ else
+ local b = pop_opstack()
+ if not b then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb = a[1], b[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ gsstate.matrix = do_concat(gsstate.matrix, { b[4], 0, 0, a[4], 0, 0 })
+ end
+ return true
+end
+
+function operators.concat()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= "array" then
+ return ps_error('typecheck')
+ end
+ if a[6] ~= 6 then
+ return ps_error('typecheck')
+ end
+ local thearray = get_VM(a[4])
+ local l = { }
+ for i=1,#thearray do
+ local v = thearray[i]
+ local t = v[1]
+ if not (t == 'real' or t == 'integer') then
+ return ps_error('typecheck')
+ end
+ l[i] = v[4]
+ end
+ gsstate.matrix = do_concat(gsstate.matrix,l)
+ return true
+end
+
+function operators.concatmatrix()
+ local tf = pop_opstack()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if tf[1] ~= "array" then return ps_error('typecheck') end
+ if b [1] ~= "array" then return ps_error('typecheck') end
+ if a [1] ~= "array" then return ps_error('typecheck') end
+ if tf[6] ~= 6 then return ps_error('typecheck') end
+ if b [6] ~= 6 then return ps_error('typecheck') end
+ if a [6] ~= 6 then return ps_error('typecheck') end
+ local al = { }
+ local thearray = get_VM(a[4])
+ for i=1,#thearray do
+ local v = thearray[i]
+ local tv = v[1]
+ if not (tv == 'real' or tv == 'integer') then
+ return ps_error('typecheck')
+ end
+ al[i] = v[4]
+ end
+ local bl = { }
+ local thearray = get_VM(b[4])
+ for i=1,#thearray do
+ local v = thearray[i]
+ local tv = v[1]
+ if not (tv == 'real' or tv == 'integer') then
+ return ps_error('typecheck')
+ end
+ bl[i] = v[4]
+ end
+ local c = do_concat(al, bl)
+ local m = VM[tf[4]]
+ for i=1,6 do
+ m[i] = { 'real', 'unlimited', 'literal', c[i] }
+ end
+ tf[5] = 6
+ push_opstack(tf)
+ return true
+end
+
+function operators.rotate()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ if ta == 'array' then
+ local tf
+ if a[6] ~= 6 then
+ return ps_error('typecheck')
+ end
+ tf = a
+ a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if not (a[1] == 'real' or a[1] == 'integer') then
+ return ps_error('typecheck')
+ end
+ local m = VM[tf[4]]
+ local old = { m[1][4], m[2][4], m[3][4], m[4][4], m[5][4], m[6][4] }
+ local av = a[4]
+ local c = do_concat (old, {cos(rad(av)),sin(rad(av)),-sin(rad(av)),cos(rad(av)), 0, 0})
+ for i=1,6 do
+ m[i] = { 'real', 'unlimited', 'literal', c[i] }
+ end
+ push_opstack(tf)
+ elseif ta == 'real' or ta == 'integer' then
+ local av = a[4]
+ gsstate.matrix = do_concat(gsstate.matrix,{cos(rad(av)),sin(rad(av)),-sin(rad(av)),cos(rad(av)),0,0})
+ else
+ return ps_error('typecheck')
+ end
+ return true
+end
+
+function operators.transform()
+ local a = pop_opstack()
+ local b = pop_opstack()
+ if not b then
+ ps_error('stackunderflow')
+ end
+ local tf
+ if a[1] == 'array' then
+ if a[6] ~= 6 then
+ return ps_error('typecheck')
+ end
+ local thearray = get_VM(a[4])
+ tf = { }
+ for i=1,#thearray do
+ local v = thearray[i]
+ local v1 = v[1]
+ if not (v1 == 'real' or v1 == 'integer') then
+ return ps_error('typecheck')
+ end
+ tf[i] = v[4]
+ end
+ a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ else
+ tf = gsstate.matrix
+ end
+ local a1 = a[1]
+ local b1 = b[1]
+ if not (a1 == 'real' or a1 == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (b1 == 'real' or b1 == 'integer') then
+ return ps_error('typecheck')
+ end
+ local x, y = do_transform(tf,b[4],a[4]);
+ push_opstack { 'real', 'unlimited', 'literal', x }
+ push_opstack { 'real', 'unlimited', 'literal', y }
+ return true
+end
+
+local function commontransform()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local tf
+ if a[1] == 'array' then
+ if a[6] ~= 6 then
+ return ps_error('typecheck')
+ end
+ tf = { }
+ local thearray = get_VM(a[4])
+ for i=1,#thearray do
+ local v = thearray[i]
+ local tv = v[1]
+ if not (tv == 'real' or tv == 'integer') then
+ return ps_error('typecheck')
+ end
+ tf[i] = v[4]
+ end
+ a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ else
+ tf = gsstate.matrix
+ end
+ local b = pop_opstack()
+ if not b then
+ return ps_error('stackunderflow')
+ end
+ local ta = a[1]
+ local tb = b[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ return true, tf, a, b
+end
+
+function operators.dtransform()
+ local ok, tf, a, b = commontransform()
+ if ok then
+ local x, y = do_transform({tf[1],tf[2],tf[3],tf[4],0,0},b[4],a[4])
+ if not x then
+ return ps_error('undefinedresult')
+ end
+ push_opstack { 'real', 'unlimited', 'literal', x }
+ push_opstack { 'real', 'unlimited', 'literal', y }
+ return true
+ else
+ return false, tf
+ end
+end
+
+function operators.itransform()
+ local ok, tf, a, b = commontransform()
+ if ok then
+ local x, y = do_itransform(tf,b[4],a[4])
+ if not x then
+ return ps_error('undefinedresult')
+ end
+ push_opstack { 'real', 'unlimited', 'literal', x }
+ push_opstack { 'real', 'unlimited', 'literal', y }
+ return true
+ else
+ return false, tf
+ end
+end
+
+function operators.idtransform()
+ local ok, tf, a, b = commontransform()
+ if ok then
+ local x,y = do_itransform({tf[1],tf[2],tf[3],tf[4],0,0},b[4],a[4]);
+ if not x then
+ return ps_error('undefinedresult')
+ end
+ push_opstack { 'real', 'unlimited', 'literal', x }
+ push_opstack { 'real', 'unlimited', 'literal', y }
+ return true
+ else
+ return false, tf
+ end
+end
+
+function operators.invertmatrix()
+ local tf = pop_opstack()
+ if not tf then
+ return ps_error('stackunderflow')
+ end
+ if tf[1] ~= "array" then
+ return ps_error('typecheck')
+ end
+ if tf[6] ~= 6 then
+ return ps_error('typecheck')
+ end
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= "array" then
+ return ps_error('typecheck')
+ end
+ if a[6] ~= 6 then
+ return ps_error('typecheck')
+ end
+ local al = { }
+ local thearray = get_VM(a[4])
+ for i=1,#thearray do
+ local v = thearray[i]
+ local tv = v[1]
+ if not (tv == 'real' or tv == 'integer') then
+ return ps_error('typecheck')
+ end
+ al[i] = v[4]
+ end
+ local c = do_inverse(al)
+ if not c then
+ return ps_error('undefinedresult')
+ end
+ local m = VM[tf[4]]
+ for i=1,6 do
+ m[i] = { 'real', 'unlimited', 'literal', c[i] }
+ end
+ tf[5] = 6
+ push_opstack(tf)
+ return true
+end
+
+-- Path construction operators
+--
+-- +newpath +currentpoint +moveto +rmoveto +lineto +rlineto +arc +arcn +arcto +curveto +rcurveto
+-- +closepath +flattenpath -reversepath -strokepath -charpath +clippath -pathbbox -pathforall
+-- +initclip *clip *eoclip
+
+function operators.newpath()
+ gsstate.path = { }
+ gsstate.position = { }
+ return true
+end
+
+function operators.currentpoint()
+ local position = gsstate.position
+ if #position == 0 then
+ return ps_error('nocurrentpoint')
+ end
+ local x, y = do_itransform(gsstate.matrix, position[1], position[2])
+ if not x then
+ return ps_error('undefinedresult')
+ end
+ push_opstack { 'real', 'unlimited', 'literal', x }
+ push_opstack { 'real', 'unlimited', 'literal', y }
+end
+
+function operators.moveto()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local b1 = b[1]
+ local a1 = a[1]
+ if not (b1 == 'real' or b1 == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (a1 == 'real' or a1 == 'integer') then
+ return ps_error('typecheck')
+ end
+ local path = gsstate.path
+ local length = #path
+ local x, y = do_transform(gsstate.matrix, a[4], b[4])
+ if length > 0 and path[length][1] == "moveto" then
+ -- replace last moveto
+ else
+ length = length + 1
+ end
+ path[length] = { "moveto", x, y }
+ gsstate.position = { x, y }
+ return true
+end
+
+function operators.rmoveto()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local bt = b[1]
+ local at = a[1]
+ if not (bt == 'real' or bt == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (at == 'real' or at == 'integer') then
+ return ps_error('typecheck')
+ end
+ local position = gsstate.position
+ local path = gsstate.path
+ local length = #path
+ if #position == 0 then
+ return ps_error('nocurrentpoint')
+ end
+ local x, y = do_transform(gsstate.matrix, a[4], b[4])
+ x = position[1] + x
+ y = position[2] + y
+ position[1] = x
+ position[2] = y
+ if length > 0 and path[length][1] == "moveto" then
+ -- replace last moveto
+ else
+ length = length + 1
+ end
+ path[length] = { "moveto", x, y }
+ return true
+end
+
+function operators.lineto()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local at = a[1]
+ local bt = b[1]
+ if not (bt == 'real' or bt == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (at == 'real' or at == 'integer') then
+ return ps_error('typecheck')
+ end
+ local position = gsstate.position
+ local path = gsstate.path
+ local length = #path
+ if #position == 0 then
+ return ps_error('nocurrentpoint')
+ end
+ local x, y = do_transform(gsstate.matrix, a[4], b[4])
+ gsstate.position = { x, y }
+ path[length+1] = { "lineto", x, y }
+ return true
+end
+
+function operators.rlineto()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local at = a[1]
+ local bt = b[1]
+ if not (bt == 'real' or bt == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (at == 'real' or at == 'integer') then
+ return ps_error('typecheck')
+ end
+ local position = gsstate.position
+ local path = gsstate.path
+ local length = #path
+ if #position == 0 then
+ return ps_error('nocurrentpoint')
+ end
+ local x, y = do_transform(gsstate.matrix, a[4], b[4])
+ x = position[1] + x
+ y = position[2] + y
+ position[1] = x
+ position[2] = y
+ path[length+1] = { "lineto", x, y }
+ return true
+end
+
+local function arc_to_curve (x, y, r, aa, theta)
+ local th = rad(theta/2.0)
+ local x0 = cos(th)
+ local y0 = sin(th)
+ local x1 = (4.0-x0)/3.0
+ local y1 = ((1.0-x0)*(3.0-x0))/(3.0*y0) -- y0 != 0...
+ local x2 = x1
+ local y2 = -y1
+ -- local x3 = x0
+ -- local y3 = -y0
+
+ local bezAng = rad(aa) + th
+ local cBezAng = cos(bezAng)
+ local sBezAng = sin(bezAng)
+
+ local rx0 = (cBezAng * x0) - (sBezAng * y0)
+ local ry0 = (sBezAng * x0) + (cBezAng * y0)
+ local rx1 = (cBezAng * x1) - (sBezAng * y1)
+ local ry1 = (sBezAng * x1) + (cBezAng * y1)
+ local rx2 = (cBezAng * x2) - (sBezAng * y2)
+ local ry2 = (sBezAng * x2) + (cBezAng * y2)
+ -- local rx3 = (cBezAng * x3) - (sBezAng * y3)
+ -- local ry3 = (sBezAng * x3) + (cBezAng * y3)
+
+ local px0 = x + r*rx0
+ local py0 = y + r*ry0
+ local px1 = x + r*rx1
+ local py1 = y + r*ry1
+ local px2 = x + r*rx2
+ local py2 = y + r*ry2
+ -- local px3 = x + r*rx3
+ -- local py3 = y + r*ry3
+
+ return px2, py2, px1, py1, px0, py0 -- no px3, py3
+end
+
+local function arc_start(x,y,r,aa)
+ local x3 = 1
+ local y3 = 0
+ local bezAng = rad(aa)
+ local cBezAng = cos(bezAng)
+ local sBezAng = sin(bezAng)
+ local rx3 = (cBezAng * x3) - (sBezAng * y3)
+ local ry3 = (sBezAng * x3) + (cBezAng * y3)
+ local px3 = x + r*rx3
+ local py3 = y + r*ry3
+ return px3, py3
+end
+
+local function do_arc(matrix,path,x,y,r,aa,ab)
+ local endx, endy
+ local segments = floor((ab-aa+44.999999999)/45)
+ if segments == 0 then
+ return do_transform(gsstate.matrix, x,y)
+ end
+ local theta = (ab-aa) / segments
+ while segments>0 do
+ local x1, y1, x2, y2, x3, y3 = arc_to_curve(x,y,r,aa,theta)
+ local px2, py2 = do_transform(matrix,x2,y2)
+ local px1, py1 = do_transform(matrix,x1,y1)
+ endx, endy = do_transform(matrix, x3,y3)
+ path[#path+1] = { "curveto", px1, py1, px2, py2, endx, endy }
+ segments = segments - 1
+ aa = aa + theta
+ end
+ return endx, endy
+end
+
+local function do_arcn(matrix,path,x,y,r,aa,ab)
+ local endx, endy
+ local segments = floor((aa-ab+44.999999999)/45)
+ if segments == 0 then
+ return do_transform(matrix, x,y)
+ end
+ local theta = (aa-ab) / segments
+ while segments > 0 do
+ local x1, y1, x2, y2, x3, y3 = arc_to_curve(x,y,r,aa,-theta)
+ local px1, py1 = do_transform(matrix,x1,y1)
+ local px2, py2 = do_transform(matrix,x2,y2)
+ endx, endy = do_transform(matrix,x3,y3)
+ path[#path+1] = { "curveto", px1 , py1 , px2 , py2 , endx , endy }
+ segments = segments - 1
+ aa = aa - theta
+ end
+ return endx, endy
+end
+
+local function commonarc(action)
+ local e = pop_opstack()
+ local d = pop_opstack()
+ local c = pop_opstack()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb, tc, td, te = a[1], b[1], c[1], d[1], e[1], f[1]
+ if not (ta == 'real' or ta == 'integer') then return ps_error('typecheck') end
+ if not (tb == 'real' or tb == 'integer') then return ps_error('typecheck') end
+ if not (tc == 'real' or tc == 'integer') then return ps_error('typecheck') end
+ if not (td == 'real' or td == 'integer') then return ps_error('typecheck') end
+ if not (te == 'real' or te == 'integer') then return ps_error('typecheck') end
+ local position = gsstate.position
+ local path = gsstate.path
+ local matrix = gsstate.matrix
+ local vd = d[4]
+ local ve = e[4]
+ if vd < 0 or ve < 0 or vd > 360 or ve > 360 or (vd-ve) <= 0 then
+ return ps_error('limitcheck')
+ end
+ local r = c[4]
+ if r == 0 then
+ ps_error('limitcheck')
+ end
+ local x = a[4]
+ local y = b[4]
+ local x0, y0 = arc_start(x,y,r,vd) -- find starting points
+ local startx, starty = do_transform(matrix,x0,y0)
+ path[#path+1] = { #position == 2 and "lineto" or "moveto", startx, starty }
+ position[1], position[2] = action(matrix,path,x,y,r,vd,ve)
+ return true
+end
+
+function operators.arc()
+ commonarc(do_arc)
+end
+
+function operators.arcn()
+ commonarc(do_arcn)
+end
+
+local function vlength (a,b)
+ return sqrt(a^2+b^2)
+end
+
+local function vscal_ (a,b,c)
+ return a*b, a*c
+end
+
+-- this is of_the_way
+
+local function between (dist, pa, pb)
+ local pa1, pa2 = pa[1], pa[2]
+ local pb1, pb2 = pb[1], pb[2]
+ return {
+ pa1 + dist * (pb1 - pa1),
+ pa2 + dist * (pb2 - pa2),
+ }
+end
+
+local function sign (a)
+ return a < 0 and -1 or 1
+end
+
+local function do_arcto(x,y,r) -- todo: check with original
+ local h = gsstate.position
+ local tx1, tx2, ty1, ty2
+ local c1, c2
+ local x1, x2 = x[1], x[2]
+ local y1, y2 = y[1], y[2]
+ local h1, h2 = h[1], h[2]
+ local ux, uy = x1 - h1, x2 - h2
+ local vx, vy = y1 - x1, y2 - x2
+ local lx, ly = vlength(ux,uy), vlength(vx,vy)
+ local sx, sy = ux*vy - uy*vx, ux*vx + uy*vy
+ if sx == 0 and sy == 0 then
+ sx = r
+ sy = 0
+ else
+ sx = r
+ sy = atan2(sx,sy)
+ end
+ local a_arcto = sx*tan(abs(sy)/2)
+ if sx*sy*lx*ly == 0 then
+ tx1 = x1
+ tx2 = x2
+ ty1 = x1
+ ty2 = x2
+ c1 = x1
+ c2 = x2
+ else
+ local tx = between(a_arcto/lx,x,h)
+ local ty = between(a_arcto/ly,x,y)
+ local cc, dd = vscal_(sign(sy)*sx/lx,-uy,ux)
+ tx1 = tx[1]
+ tx2 = tx[2]
+ ty1 = ty[1]
+ ty2 = ty[2]
+ c1 = tx1 + cc
+ c2 = tx2 + dd
+ end
+ -- now tx is the starting point, ty is the endpoint,
+ -- c is the center of the circle. find the two angles
+ local anga = deg(atan2(tx2-c2,tx1-c1)) -- todo, -90 is wrong
+ local angb = deg(atan2(ty2-c2,ty1-c1)) -- todo, -90 is wrong
+ return c1, c2, r, anga, angb, tx1, tx2, ty1, ty2
+end
+
+function operators.arcto()
+ local e = pop_opstack()
+ local d = pop_opstack()
+ local c = pop_opstack()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb, tc, td, te = a[1], b[2], c[1], d[1], e[1]
+ if not (ta == 'real' or ta == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (tc == 'real' or tc == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (td == 'real' or td == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (te == 'real' or te == 'integer') then
+ return ps_error('typecheck')
+ end
+ local x1, y1, x2, y2, r = a[4], b[4], c[4], d[4], e[4]
+ local position = gsstate.position
+ local path = gsstate.path
+ if #position == 0 then
+ return ps_error('nocurrentpoint')
+ end
+ local x, y, r, anga, angb, tx1, tx2, ty1, ty2 = do_arcto({x1,y1},{x2, y2},r)
+ local vx, vy = do_transform(gsstate.matrix,tx1,tx2)
+ path[#path+1] = { "lineto", vx, vy }
+ if anga == angb then
+ -- do nothing
+ elseif anga > angb then
+ position[1], position[2] = do_arcn(x,y,r,anga,angb)
+ else
+ position[1], position[2] = do_arc (x,y,r,anga,angb)
+ end
+ push_opstack { 'real', 'unlimited', 'literal', tx1 }
+ push_opstack { 'real', 'unlimited', 'literal', tx2 }
+ push_opstack { 'real', 'unlimited', 'literal', ty1 }
+ push_opstack { 'real', 'unlimited', 'literal', ty2 }
+end
+
+function operators.curveto()
+ local f = pop_opstack()
+ local e = pop_opstack()
+ local d = pop_opstack()
+ local c = pop_opstack()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local f1 = f[1] if not (f1 == 'real' or f1 == 'integer') then return ps_error('typecheck') end
+ local e1 = e[1] if not (e1 == 'real' or e1 == 'integer') then return ps_error('typecheck') end
+ local d1 = d[1] if not (d1 == 'real' or d1 == 'integer') then return ps_error('typecheck') end
+ local c1 = c[1] if not (c1 == 'real' or c1 == 'integer') then return ps_error('typecheck') end
+ local b1 = b[1] if not (b1 == 'real' or b1 == 'integer') then return ps_error('typecheck') end
+ local a1 = a[1] if not (a1 == 'real' or a1 == 'integer') then return ps_error('typecheck') end
+ --
+ if #gsstate.position == 0 then
+ return ps_error('nocurrentpoint')
+ end
+ --
+ local matrix = gsstate.matrix
+ local x, y = do_transform(matrix, e[4], f[4])
+ local ax, ay = do_transform(matrix, a[4], b[4])
+ local bx, by = do_transform(matrix, c[4], d[4])
+ gsstate.position = { x, y }
+ --
+ local path = gsstate.path
+ path[#path+1] = { "curveto", ax, ay, bx, by, x, y }
+ return true
+end
+
+function operators.rcurveto()
+ local f = pop_opstack()
+ local e = pop_opstack()
+ local d = pop_opstack()
+ local c = pop_opstack()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ft if not (ft == 'real' or ft == 'integer') then return ps_error('typecheck') end
+ local et if not (et == 'real' or et == 'integer') then return ps_error('typecheck') end
+ local dt if not (dt == 'real' or dt == 'integer') then return ps_error('typecheck') end
+ local ct if not (ct == 'real' or ct == 'integer') then return ps_error('typecheck') end
+ local bt if not (bt == 'real' or bt == 'integer') then return ps_error('typecheck') end
+ local at if not (at == 'real' or at == 'integer') then return ps_error('typecheck') end
+ local position = gsstate.position
+ local path = gsstate.path
+ if #position == 0 then
+ return ps_error('nocurrentpoint')
+ end
+ local x, y = do_transform(matrix, e[4], f[4])
+ local ax, ay = do_transform(matrix, a[4], b[4])
+ local bx, by = do_transform(matrix, c[4], d[4])
+ local px = position[1] + x
+ local py = position[2] + y
+ path[#path+1] = {
+ "curveto",
+ position[1] + ax,
+ position[2] + ay,
+ position[1] + bx,
+ position[2] + by,
+ px,
+ py
+ }
+ position[1] = px
+ position[2] = py
+ return true
+end
+
+function operators.closepath()
+ local path = gsstate.path
+ local length = #path
+ if length > 0 and path[length][1] ~= 'closepath' then
+ local m = path[1]
+ local a = m[2]
+ local b = m[3]
+ local x, y = do_transform(gsstate.matrix, a, b)
+ gsstate.position = { x, y }
+ path[length+1] = { "closepath", x, y }
+ end
+ return true
+end
+
+-- finds a point on a bezier curve
+-- P(x,y) = (1-t)^3*(x0,y0)+3*(1-t)^2*t*(x1,y1)+3*(1-t)*t^2*(x2,y2)+t^3*(x3,y3)
+
+local function bezier_at(t,x0,y0,x1,y1,x2,y2,x3,y3)
+ local v = (1 - t)
+ local x = (v^3)*x0 + 3*(v^2)*t*x1 + 3*v*(t^2)*x2 + (t^3)*x3
+ local y = (v^3)*y0 + 3*(v^2)*t*y1 + 3*v*(t^2)*y2 + (t^3)*y3
+ return x, y
+end
+
+local delta = 10 -- 100
+
+local function good_enough (flatness,c,ct1,ct2,l)
+ local c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y = c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8]
+ local l0x, l0y, l1x, l1y = l[1], l[2], l[3], l[4]
+ local t = 0
+ while t < delta do
+ local td = t/delta
+ local bx, by = bezier_at(ct1+(ct2-ct1)*td,c0x,c0y,c1x,c1y,c2x,c2y,c3x,c3y)
+ local lx, ly = (1-td)*l0x + td*l1x, (1-td)*l0y + td*l1y
+ local dist = vlength(bx-lx,by-ly)
+ if dist > flatness then
+ return false
+ end
+ t = t + 1
+ end
+ return true
+end
+
+-- argument d is recursion depth, 10 levels should be enough to reach a conclusion
+-- (and already generates over 1000 lineto's in the worst case)
+
+local function splitter (flatness,p,d,c,ct1,ct2,l)
+ local c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y = c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8]
+ d = d + 1
+ local r = good_enough(flatness,c,ct1,ct1+ct2,l)
+ if r or d > 10 then
+ p[#p + 1] = { 'lineto', l[3], l[4] }
+ else
+ local ct22 = ct2/2
+ local l2x, l2y = bezier_at(ct1+ct22,c0x,c0y,c1x,c1y,c2x,c2y,c3x,c3y)
+ local l1 = { l[1], l[2], l2x, l2y }
+ local l2 = { l2x, l2y, l[3], l[4] }
+ splitter(flatness,p,d,c,ct1,ct22,l1)
+ splitter(flatness,p,d,c,ct1+ct22,ct22,l2)
+ end
+end
+
+local function flattencurve( homex, homey, curve, flatness)
+ local p = { }
+ local c6 = curve[6]
+ local c7 = curve[7]
+ local thecurve = { homex, homey, curve[2], curve[3], curve[4], curve[5], c6, c7 }
+ local theline = { homex, homey, c6, c7 }
+ splitter(flatness, p, 0, thecurve, 0, 1, theline)
+ return p
+end
+
+local function do_flattenpath (p, flatness)
+ local x, y
+ local px = { }
+ local nx = 0
+ -- we don't care about differences less than a a permille of a point, ever
+ if flatness < 0.001 then
+ flatness = 0.001
+ end
+ if p then
+ for i=1,#p do
+ local v = p[i]
+ local t = v[1]
+ if t == "curveto" then
+ local pxl = flattencurve(x,y,v,flatness)
+ for i=1,#pxl do
+ nx = nx + 1 ; px[nx] = pxl[i]
+ end
+ x, y = v[6], v[7]
+ elseif t == "lineto" or t == "moveto" then
+ x, y = v[2], v[3]
+ nx = nx + 1 ; px[nx] = v
+ else
+ nx = nx + 1 ; px[nx] = v
+ end
+ end
+ end
+ return px
+end
+
+function operators.flattenpath()
+ gsstate.path = do_flattenpath(gsstate.path,gsstate.flatness)
+end
+
+function operators.clippath()
+ gsstate.path = gsstate.clip
+ return true
+end
+
+function operators.initclip()
+ device.initclip()
+ return true
+end
+
+function operators.eofill()
+ local color = gsstate.color
+ local thecolor = color[color.type]
+ if type(thecolor) == "table" then
+ thecolor = { unpack(thecolor) }
+ end
+ currentpage[#currentpage+1] = {
+ type = 'eofill',
+ path = gsstate.path,
+ colortype = color.type,
+ color = thecolor,
+ }
+ operators.newpath()
+ return true
+end
+
+-- todo: this only fixes the output, not the actual clipping path
+-- in the gsstate !
+
+function operators.clip()
+ currentpage[#currentpage+1] = {
+ type = 'clip',
+ path = gsstate.path,
+ }
+ return true
+end
+
+-- todo: this only fixes the output, not the actual clipping path
+-- in the gsstate !
+
+function operators.eoclip()
+ currentpage[#currentpage+1] = {
+ type = 'eoclip',
+ path = gsstate.path,
+ }
+ return true
+end
+
+-- Painting operators
+--
+-- +erasepage +fill +eofill +stroke -image -imagemask
+
+-- general graphics todo: transfer function, flatness
+
+function operators.erasepage()
+ currentpage = { }
+ return true
+end
+
+function operators.stroke()
+ local color = gsstate.color
+ local ctype = color.type
+ local thecolor = color[ctype]
+ -- if type(thecolor) == "table" then
+ -- thecolor = { unpack(thecolor) }
+ -- end
+ currentpage[#currentpage+1] = {
+ type = 'stroke',
+ path = gsstate.path,
+ colortype = ctype,
+ color = thecolor,
+ miterlimit = gsstate.miterlimit,
+ linewidth = gsstate.linewidth,
+ linecap = gsstate.linecap,
+ linejoin = gsstate.linejoin,
+ -- dashpattern = { unpack (gsstate.dashpattern) }, -- unpack? we don't manipulate
+ dashpattern = gsstate.dashpattern,
+ dashoffset = gsstate.dashoffset
+ }
+ operators.newpath()
+ return true
+end
+
+function operators.fill()
+ local color = gsstate.color
+ local ctype = color.type
+ local thecolor = color[ctype]
+ -- if type(thecolor) == "table" then
+ -- thecolor = { unpack(thecolor) }
+ -- end
+ currentpage[#currentpage+1] = {
+ type = 'fill',
+ path = gsstate.path,
+ colortype = ctype,
+ color = thecolor,
+ }
+ operators.newpath()
+ return true
+end
+
+-- Device setup and output operators
+--
+-- +showpage +copypage +banddevice +framedevice +nulldevice +renderbands
+
+-- will be replaced by the argument of 'new'
+
+-- this reports the bounding box of a page
+
+-- todo: linewidth for strokes
+-- todo: clips
+-- todo: strings (width&height)
+
+local calculatebox = false
+
+initializers[#initializers+1] = function()
+ calculatebox = true
+end
+
+local function boundingbox(page)
+
+ local bounding = specials.boundingbox
+ if bounding and not calculatebox then
+ return unpack(bounding)
+ end
+
+ local minx, miny, maxx, maxy
+ local startx, starty
+ local linewidth
+
+ local function update_bbox (x,y)
+ if not minx then
+ minx = x
+ miny = y
+ maxx = x
+ maxy = y
+ end
+ if linewidth then
+ local xx = x + linewidth/2
+ if xx > maxx then maxx = xx elseif xx < minx then minx = xx end
+ local xx = x - linewidth/2
+ if xx > maxx then maxx = xx elseif xx < minx then minx = xx end
+ local yy = y + linewidth/2
+ if yy > maxy then maxy = yy elseif yy < miny then miny = yy end
+ local yy = y - linewidth/2
+ if yy > maxy then maxy = yy elseif yy < miny then miny = yy end
+ else
+ if x > maxx then maxx = x elseif x < minx then minx = x end
+ if y > maxy then maxy = y elseif y < miny then miny = y end
+ end
+ startx, starty = x, y
+ end
+
+ for i=1,#page do
+ local object = page[i]
+ local p = do_flattenpath(object.path,0.5)
+ linewidth = object.type == "stroke" and object.linewidth
+ for i=1,#p do
+ local segment = p[i]
+ local type = segment[1]
+ if type == "lineto" then
+ if startx then
+ update_bbox(startx,starty)
+ end
+ update_bbox(segment[2],segment[3])
+ elseif type == "curveto" then
+ if startx then
+ update_bbox(startx,starty)
+ end
+ update_bbox(segment[6],segment[7])
+ elseif type == "moveto" then
+ startx, starty = segment[2], segment[3]
+ end
+ end
+ end
+ if minx then
+ return minx, miny, maxx, maxy
+ else
+ return 0, 0, 0, 0
+ end
+end
+
+------------------------------------------------------------------
+
+local function boundingbox (page)
+
+ local bounding = specials.boundingbox
+ if bounding and not calculatebox then
+ return unpack(bounding)
+ end
+
+ local minx, miny, maxx, maxy
+ local startx, starty
+ local linewidth
+
+ local function update_bbox (x,y)
+ if not minx then
+ minx = x
+ miny = y
+ maxx = x
+ maxy = y
+ end
+ if linewidth then
+ local xx = x + linewidth/2
+ if xx > maxx then
+ maxx = xx
+ elseif xx < minx then
+ minx = xx
+ end
+ local xx = x - linewidth/2
+ if xx > maxx then
+ maxx = xx
+ elseif xx < minx then
+ minx = xx
+ end
+ local yy = y + linewidth/2
+ if yy > maxy then
+ maxy = yy
+ elseif yy < miny then
+ miny = yy
+ end
+ local yy = y - linewidth/2
+ if yy > maxy then
+ maxy = yy
+ elseif yy < miny then
+ miny = yy
+ end
+ else
+ if x > maxx then
+ maxx = x
+ elseif x < minx then
+ minx = x
+ end
+ if y > maxy then
+ maxy = y
+ elseif y < miny then
+ miny = y
+ end
+ end
+ startx, starty = x, y
+ end
+
+ local delta = 10 -- 100
+
+ local function good_enough (ct1,ct2, c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y, l0x, l0y, l1x, l1y)
+ local t = 0
+ while t < delta do
+ local td = t/delta
+ local bx, by = bezier_at(ct1+(ct2-ct1)*td,c0x,c0y,c1x,c1y,c2x,c2y,c3x,c3y)
+ local lx, ly = (1-td)*l0x + td*l1x, (1-td)*l0y + td*l1y
+ local dist = sqrt((bx-lx)^2+(by-ly)^2) -- vlength(bx-lx,by-ly)
+ if dist > 0.5 then
+ return false
+ end
+ t = t + 1
+ end
+ return true
+ end
+
+ local function splitter (d,ct1,ct2, c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y, l0x, l0y, l1x, l1y)
+ d = d + 1
+ local r = good_enough(ct1,ct1+ct2, c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y, l0x, l0y, l1x, l1y)
+ if r or d > 10 then
+ if startx then
+ update_bbox(l1x, l1y)
+ end
+ else
+ local ct22 = ct2/2
+ local l2x, l2y = bezier_at(ct1+ct22,c0x,c0y,c1x,c1y,c2x,c2y,c3x,c3y)
+ splitter(d,ct1, ct22, c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y, l0x, l0y, l2x, l2y)
+ splitter(d,ct1+ct22,ct22, c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y, l2x, l2y, l1x, l1y)
+ end
+ end
+
+ for i=1,#page do
+ local object = page[i]
+ local p = object.path
+ linewidth = object.type == "stroke" and object.linewidth
+ for i=1,#p do
+ local segment = p[i]
+ local type = segment[1]
+ if type == "lineto" then
+ if startx then
+ update_bbox(startx,starty)
+ end
+ update_bbox(segment[2],segment[3])
+ elseif type == "curveto" then
+ local c6 = segment[6]
+ local c7 = segment[7]
+ splitter(0, 0, 1, startx, starty, segment[2], segment[3], segment[4], segment[5], c6, c7, startx, starty, c6, c7)
+ elseif type == "moveto" then
+ startx, starty = segment[2], segment[3]
+ end
+ end
+ end
+ if minx then
+ return minx, miny, maxx, maxy
+ else
+ return 0, 0, 0, 0
+ end
+end
+
+------------------------------------------------------------------
+
+-- most time is spend in calculating the boundingbox
+
+-- NULL output
+
+devices.null = {
+ initgraphics = function() gsstate.matrix = { 1, 0, 0, 1, 0, 0 } end,
+ initclip = function() gsstate.clip = { } end,
+ showpage = function() return "" end,
+}
+
+-- PDF output
+
+local pdf = {
+ initgraphics = function() gsstate.matrix = { 1, 0, 0, 1, 0, 0 } end,
+ initclip = function() gsstate.clip = { } end,
+ -- startpage = function(llc,lly,urx,ury) end,
+ -- flushpage = function() end,
+ -- stoppage = function() end,
+}
+
+devices.pdf = pdf
+
+function pdf.showpage(page)
+ --
+ local startpage = pdf.startpage
+ local stoppage = pdf.stoppage
+ local flushpage = pdf.flushpage
+ local showfont = pdf.showfont
+ --
+ if not flushpage then
+ return
+ end
+ --
+ if startpage then
+ startpage(boundingbox(page))
+ end
+ --
+ local t = { "q" }
+ local n = 1
+ local g_colortype = "notacolor"
+ local g_color = ""
+ local g_miterlimit = -1
+ local g_linejoin = -1
+ local g_linecap = -1
+ local g_linewidth = -1
+ local g_dashpattern = nil
+ local g_dashoffset = -1
+ local flush = devices.pdf.flush
+ for i=1,#page do
+ local object = page[i]
+ local path = object.path
+ local otyp = object.type
+ if otype ~= "clip" and otype ~= "eoclip" then
+ local colortype = object.colortype
+ local color = object.color
+ if colortype == "gray" then
+ local v = formatters["%f g %f G"](color,color)
+ if g_color ~= v then
+ g_colortype = "gray"
+ g_color = v
+ n = n + 1 ; t[n] = v
+ end
+ elseif colortype == "rgb" then
+ local r, g, b = color[1], color[2], color[3]
+ local v = formatters["%f %f %f rg %f %f %f RG"](r,g,b,r,g,b)
+ if g_color ~= v then
+ g_colortype = "rgb"
+ g_color = v
+ n = n + 1 ; t[n] = v
+ end
+ elseif colortype == "cmyk" then
+ local c, m, y, k = color[1], color[2], color[3], color[4]
+ local v = formatters["%f %f %f %f k %f %f %f %f K"](c,m,y,k,c,m,y,k)
+ if g_color ~= v then
+ g_colortype = "cmyk"
+ g_color = v
+ n = n + 1 ; t[n] = v
+ end
+ elseif colortype == "hsb" then
+ local r, g, b = hsv_to_rgb(color[1],color[2],color[3])
+ local v = formatters["%f %f %f rg %f %f %f RG"](r,g,b,r,g,b)
+ if g_color ~= v then
+ g_colortype = "rgb"
+ g_color = v
+ n = n + 1 ; t[n] = v
+ end
+ end
+ end
+ if otype == "stroke" then
+ local miterlimit = object.miterlimit
+ if g_miterlimit ~= miterlimit then
+ g_miterlimit = miterlimit
+ n = n + 1 ; t[n] = formatters["%f M"](miterlimit)
+ end
+ local linejoin = object.linejoin
+ if g_linejoin ~= linejoin then
+ g_linejoin = linejoin
+ n = n + 1 ; t[n] = formatters["%d j"](linejoin)
+ end
+ local linecap = object.linecap
+ if g_linecap ~= linecap then
+ g_linecap = linecap
+ n = n + 1 ; t[n] = formatters["%d J"](linecap)
+ end
+ local linewidth = object.linewidth
+ if g_linewidth ~= linewidth then
+ g_linewidth = linewidth
+ n = n + 1 ; t[n] = formatters["%f w"](linewidth)
+ end
+ local dashpattern = object.dashpattern
+ local dashoffset = object.dashoffset
+ if g_dashpattern ~= dashpattern or g_dashoffset ~= dashoffset then
+ g_dashpattern = dashpattern
+ g_dashoffset = dashoffset
+ local l = #dashpattern
+ if l == 0 then
+ n = n + 1 ; t[n] = "[] 0 d"
+ else
+ n = n + 1 ; t[n] = formatters["[% t] %d d"](dashpattern,dashoffset)
+ end
+ end
+ end
+ if path then
+ for i=1,#path do
+ local segment = path[i]
+ local styp = segment[1]
+ if styp == "moveto" then
+ n = n + 1 ; t[n] = formatters["%f %f m"](segment[2],segment[3])
+ elseif styp == "lineto" then
+ n = n + 1 ; t[n] = formatters["%f %f l"](segment[2],segment[3])
+ elseif styp == "curveto" then
+ n = n + 1 ; t[n] = formatters["%f %f %f %f %f %f c"](segment[2],segment[3],segment[4],segment[5],segment[6],segment[7])
+ elseif styp == "closepath" then
+ n = n + 1 ; t[n] = "h"
+ else
+ report("unknown path segment type %a",styp)
+ end
+ end
+ end
+ if otyp == "stroke" then
+ n = n + 1 ; t[n] = "S"
+ elseif otyp == "fill" then
+ n = n + 1 ; t[n] = "f"
+ elseif otyp == "eofill" then
+ n = n + 1 ; t[n] = "f*"
+ elseif otyp == "clip" then
+ n = n + 1 ; t[n] = "W n"
+ elseif otyp == "eoclip" then
+ n = n + 1 ; t[n] = "W* n"
+ elseif otyp == "show" then
+ if showfont then
+ if n > 0 then
+ flushpage(concat(t,"\n"))
+ n = 0 ; t = { }
+ end
+ showfont(object)
+ end
+ else
+ -- nothing to do
+ end
+ end
+ n = n + 1 ; t[n] = "Q"
+ flushpage(concat(t,"\n"))
+ --
+ if startpage then
+ stoppage()
+ end
+end
+
+function operators.showpage()
+ local copies = lookup("#copies")
+ if copies and copies[1] == 'integer' and copies[4] >= 1 then
+ local amount = floor(copies[4])
+ local render = device.showpage
+ if render then
+ for i=1,amount do
+ render(currentpage)
+ end
+ end
+ end
+ operators.erasepage()
+ operators.initgraphics()
+ return true
+end
+
+function operators.copypage()
+ local render = device.showpage
+ if render then
+ render(currentpage)
+ end
+ return true
+end
+
+function operators.banddevice()
+ local d = pop_opstack()
+ local c = pop_opstack()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb, tc, td = a[1], b[1], c[1], d[1]
+ if not (ta == 'array' and a[5] == 6) then
+ return ps_error('typecheck')
+ end
+ if not (td == 'array' and d[3] == 'executable') then
+ return ps_error('typecheck')
+ end
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (tc == 'real' or tc == 'integer') then
+ return ps_error('typecheck')
+ end
+ local dev = device.banddevice
+ if dev then
+ dev(a,b,c,d)
+ else
+ return ps_error('undefined') -- fixed
+ end
+ return true
+end
+
+function operators.renderbands()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if not (a[1] == 'array' and a[3] == 'executable') then
+ return ps_error('typecheck')
+ end
+ local dev = device.renderbands
+ if dev then
+ dev(d)
+ else
+ return ps_error('undefined')
+ end
+ return true
+end
+
+function operators.framedevice()
+ local d = pop_opstack()
+ local c = pop_opstack()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ local ta, tb, tc, td = a[1], b[1], c[1], d[1]
+ if not (ta == 'array' and a[5] == 6) then
+ return ps_error('typecheck')
+ end
+ if not (tb == 'real' or tb == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (tc == 'real' or tc == 'integer') then
+ return ps_error('typecheck')
+ end
+ if not (td == 'array' and d[3] == 'executable') then
+ return ps_error('typecheck')
+ end
+ local dev = device.framedevice
+ if dev then
+ dev(a,b,c,d)
+ else
+ return ps_error('undefined')
+ end
+ return true
+end
+
+function operators.nulldevice()
+ gsstate.device = "null"
+ operators.initgraphics()
+ return true
+end
+
+-- Character and font operators
+--
+-- +definefont *findfont +scalefont +makefont +setfont +currentfont +show -ashow -widthshow
+-- -awidthshow +kshow -stringwidth ^FontDirectory ^StandardEncoding
+
+-- Fonts are a bit special because it is needed to cooperate with the enclosing PDF document.
+
+local FontDirectory
+
+initializers[#initializers+1] = function(reset)
+ if reset then
+ FontDirectory = nil
+ else
+ FontDirectory = add_VM {
+ access = 'unlimited',
+ size = 0,
+ maxsize = 5000,
+ dict = { },
+ }
+ end
+end
+
+-- loading actual fonts is a worryingly slow exercise
+
+local fontmap
+
+initializers[#initializers+1] = function()
+ if reset then
+ fontmap = nil
+ else
+ fontmap = {
+ ['Courier-Bold'] = 'NimbusMonL-Bold.ps',
+ ['Courier-BoldOblique'] = 'NimbusMonL-BoldObli.ps',
+ ['Courier'] = 'NimbusMonL-Regu.ps',
+ ['Courier-Oblique'] = 'NimbusMonL-ReguObli.ps',
+ ['Times-Bold'] = 'NimbusRomNo9L-Medi.ps',
+ ['Times-BoldItalic'] = 'NimbusRomNo9L-MediItal.ps',
+ ['Times-Roman'] = 'NimbusRomNo9L-Regu.ps',
+ ['Times-Italic'] = 'NimbusRomNo9L-ReguItal.ps',
+ ['Helvetica-Bold'] = 'NimbusSanL-Bold.ps',
+ ['Helvetica-BoldOblique'] = 'NimbusSanL-BoldItal.ps',
+ ['Helvetica'] = 'NimbusSanL-Regu.ps',
+ ['Helvetica-Oblique'] = 'NimbusSanL-ReguItal.ps',
+ ['Symbol'] = 'StandardSymL.ps',
+ }
+ end
+end
+
+-- this can be overwritten by the user
+
+local function findfont(fontname)
+ return fontmap[fontname]
+end
+
+-- tests required keys in a font dict
+
+local function checkfont(f)
+ -- FontMatrix
+ local matrix = f['FontMatrix']
+ if not matrix or matrix[1] ~= 'array' or matrix[5] ~= 6 then
+ return false
+ end
+ local thearray = get_VM(matrix[4])
+ for i=1,#thearray do
+ local v = thearray[i]
+ local tv = v[1]
+ if not (tv == 'real' or tv == 'integer') then
+ return false
+ end
+ end
+ -- FontType
+ local ftype = f['FontType']
+ if not ftype or ftype[1] ~= 'integer' then
+ return false
+ end
+ -- FontBBox
+ local bbox = f['FontBBox']
+ -- do not test [5] here, because it can be '1' (executable array)
+ if not bbox or bbox[1] ~= 'array' or bbox[6] ~= 4 then
+ return false
+ end
+ local thearray = get_VM(bbox[4])
+ for i=1,#thearray do
+ local v = thearray[i]
+ local tv = v[1]
+ if not (tv == 'real' or tv == 'integer') then
+ return false
+ end
+ end
+ -- Encoding
+ local bbox = f['Encoding']
+ if not bbox or bbox[1] ~= 'array' or bbox[5] ~= 256 then
+ return false
+ end
+ local thearray = get_VM(bbox[4])
+ for i=1,#thearray do
+ local v = thearray[i]
+ local tv = v[1]
+ if tv[1] ~= 'name' then
+ return false
+ end
+ end
+ return true
+end
+
+-- objects of type font as essentially the same as objects of type dict
+
+function operators.definefont()
+ local b = pop_opstack()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if b[1] ~= 'dict' then
+ return ps_error('typecheck')
+ end
+ -- force keys to be names
+ if a[1] ~= 'name' then
+ return ps_error('typecheck')
+ end
+ local fontdict = get_VM(b[4])
+ if not checkfont(fontdict.dict) then
+ return ps_error('invalidfont')
+ end
+ -- check that a FID will fit
+ if fontdict.size == fontdict.maxsize then
+ return ps_error('invalidfont')
+ end
+ fontdict.dict['FID'] = {'font', 'executable', 'literal', b[4]}
+ fontdict.size = fontdict.size + 1
+ fontdict.access = 'read-only'
+ local dict = get_VM(FontDirectory)
+ local key = get_VM(a[4])
+ if not dict.dict[key] and dict.size == dict.maxsize then
+ -- return ps_error('dictfull') -- level 1 only
+ end
+ if not dict.dict[key] then
+ dict.size = dict.size + 1
+ end
+ dict.dict[key] = fontdict.dict['FID']
+ push_opstack(b)
+ return true
+end
+
+function operators.findfont()
+ local a = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if a[1] ~= 'name' then
+ return ps_error('typecheck')
+ end
+ local fontdict = get_VM(FontDirectory)
+ local key = get_VM(a[4])
+ local dict = dict.dict
+ if not dict[key] then
+ fname = findfont(key)
+ if not fname then
+ return ps_error('invalidfont')
+ end
+ local oldfontkeys = { }
+ for k, v in next, dict do
+ oldfontkeys[i] = 1
+ end
+ report("loading font file %a",fname)
+ local theopstack = opstackptr
+ local run = formatters['/eexec {pop} def (%s) run'](fname)
+ push_execstack { '.stopped', 'unlimited', 'literal', false }
+ local curstack = execstackptr
+ push_execstack { 'string', 'unlimited', 'executable', add_VM(run), 1, #run }
+ while curstack < execstackptr do
+ do_exec()
+ end
+ if execstack[execstackptr][1] == '.stopped' then
+ pop_execstack()
+ end
+ opstackptr = theopstack
+ local fkey, ftab
+ for k, v in next, dict do
+ if not oldfontkeys[k] then
+ -- this is the new dict
+ fkey = k
+ ftab = v
+ break
+ end
+ end
+ if not fkey then
+ return ps_error('invalidfont')
+ end
+ dict[key] = ftab -- set up the user requested name as well
+ end
+ push_opstack(dict[key])
+ return true
+end
+
+local function pushscaledcopy(fontdict,matrix)
+ local olddict = fontdict.dict
+ if not checkfont(olddict) then
+ return ps_error('invalidfont')
+ end
+ local newdict = { }
+ local oldsize = fontdict.size
+ local newfontdict = {
+ dict = newdict,
+ access = 'read-only',
+ size = oldsize,
+ maxsize = oldsize,
+ }
+ for k, v in next, olddict do
+ if k == "FontMatrix" then
+ local oldmatrix = get_VM(v[4])
+ local old = {
+ oldmatrix[1][4],
+ oldmatrix[2][4],
+ oldmatrix[3][4],
+ oldmatrix[4][4],
+ oldmatrix[5][4],
+ oldmatrix[6][4],
+ }
+ local c = do_concat(old,matrix)
+ local new = {
+ { 'real', 'unlimited', 'literal', c[1] },
+ { 'real', 'unlimited', 'literal', c[2] },
+ { 'real', 'unlimited', 'literal', c[3] },
+ { 'real', 'unlimited', 'literal', c[4] },
+ { 'real', 'unlimited', 'literal', c[5] },
+ { 'real', 'unlimited', 'literal', c[6] }
+ }
+ newdict[k] = { 'array', 'unlimited', 'literal', add_VM(new), 6, 6 }
+ elseif k == "FID" then
+ -- updated later
+ else
+ newfontdict.dict[k] = v
+ end
+ end
+ local f = add_VM(newfontdict)
+ newdict['FID'] = { 'font', 'read-only', 'literal', f }
+ push_opstack { 'font', 'read-only', 'literal', f } -- share ?
+ return true
+end
+
+function operators.scalefont()
+ local s = pop_opstack()
+ local b = pop_opstack()
+ if not b then
+ return ps_error('stackunderflow')
+ end
+ if b[1] ~= 'font' then
+ return ps_error('typecheck')
+ end
+ if not (s[1] == 'integer' or s[1] == 'real') then
+ return ps_error('typecheck')
+ end
+ local scals = s[4]
+ local matrix = { scale, 0, 0, scale, 0, 0 }
+ local fontdict = get_VM(b[4])
+ return pushscaledcopy(fontdict,matrix)
+end
+
+function operators.makefont()
+ local s = pop_opstack()
+ local b = pop_opstack()
+ if not b then
+ return ps_error('stackunderflow')
+ end
+ if b[1] ~= 'font' then
+ return ps_error('typecheck')
+ end
+ if s[1] ~= 'array' then
+ return ps_error('typecheck')
+ end
+ if s[6] ~= 6 then
+ return ps_error('rangecheck')
+ end
+ local matrix = { }
+ local array = get_VM(s[4])
+ for i=1,#array do
+ local v = array[i]
+ local tv = v[1]
+ if not (tv == 'real' or tv == 'integer') then
+ return ps_error('typecheck')
+ end
+ matrix[i] = v[4]
+ end
+ local fontdict = get_VM(b[4])
+ pushscaledcopy(fontdict,matrix)
+ return true
+end
+
+function operators.setfont()
+ local b = pop_opstack()
+ if not b then
+ return ps_error('stackunderflow')
+ end
+ if b[1] ~= 'font' then
+ return ps_error('typecheck')
+ end
+ gsstate.font = b[4]
+ return true
+end
+
+-- todo: the invalidfont error is temporary. 'start' should set up at least one font in
+-- FontDirectory and assing it as the current font
+
+function operators.currentfont()
+ if not gsstate.font then
+ return ps_error('invalidfont')
+ end
+ push_opstack {'font', 'read-only', 'literal', gsstate.font }
+ return true
+end
+
+function do_show(fontdict,s)
+ local stringmatrix = { }
+ local truematrix = { }
+ local stringencoding = { }
+ --
+ local dict = fontdict.dict
+ local fontname = get_VM(dict['FontName'][4])
+ local fontmatrix = get_VM(dict['FontMatrix'][4])
+ local encoding = get_VM(dict['Encoding'][4])
+ local matrix = gsstate.matrix
+ local position = gsstate.position
+ local color = gsstate.color
+ local colortype = color.type
+ local colordata = color[colortype]
+ --
+ if fontmatrix then
+ for i=1,#fontmatrix do
+ stringmatrix[i] = fontmatrix[i][4]
+ end
+ end
+ if matrix then
+ for i=1,#matrix do
+ truematrix[i] = matrix[i]
+ end
+ end
+ if encoding then
+ for i=1,#m do
+ stringencoding[i] = get_VM(e[i][4])
+ end
+ end
+ if type(colordata) == "table" then
+ colordata = { unpack(colordata) } -- copy
+ end
+ currentpage[#currentpage+1] = {
+ type = 'show',
+ string = s,
+ fontname = fontname,
+ adjust = nil,
+ x = position[1],
+ y = position[2],
+ encoding = stringencoding,
+ fontmatrix = stringmatrix,
+ matrix = truematrix,
+ colortype = colortype,
+ color = colordata,
+ }
+ -- todo: update currentpoint, needing 'stringwidth'
+end
+
+function operators.show()
+ local s = pop_opstack()
+ if not s then
+ return ps_error('stackunderflow')
+ end
+ if s[1] ~= 'string' then
+ return ps_error('typecheck')
+ end
+ if #gsstate.position == 0 then
+ return ps_error('nocurrentpoint')
+ end
+ if not gsstate.font then
+ return ps_error('invalidfont')
+ end
+ local fontdict = get_VM(gsstate.font)
+ if fontdict.access == "noaccess" then
+ return ps_error('invalidaccess')
+ end
+ if not checkfont(fontdict.dict) then
+ return ps_error('invalidfont')
+ end
+ do_show(fontdict,get_VM(s[4]))
+end
+
+
+function operators.kshow()
+ local a = pop_opstack()
+ local b = pop_opstack()
+ if not a then
+ return ps_error('stackunderflow')
+ end
+ if b[1] ~= "array" and b[3] == 'executable' then
+ return ps_error('typecheck')
+ end
+ if b[2] == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ if not a[1] == 'string' then
+ return ps_error('typecheck')
+ end
+ if a[2] == "execute-only" or a[2] == 'noaccess' then
+ return ps_error('invalidaccess')
+ end
+ local fontdict = get_VM(gsstate.font)
+ if fontdict.access == "noaccess" then
+ return ps_error('invalidaccess')
+ end
+ if #gsstate.position == 0 then
+ return ps_error('nocurrentpoint')
+ end
+ -- ok, that were the errors
+ push_execstack { '.exit', 'unlimited', 'literal', false }
+ local curstack = execstackptr
+ if a[6] == 0 then
+ return true
+ end
+ b[7] = 'i'
+ local thestring = get_VM(a[4])
+ local v = sub(thestring,1,1)
+ thestring = sub(thestring,2,-1)
+ do_show(fontdict,v)
+ for w in gmatch(thestring,".") do
+ if stopped then
+ stopped = false
+ return false
+ end
+ push_opstack { 'integer', 'unlimited', 'literal', byte(v) }
+ push_opstack { 'integer', 'unlimited', 'literal', byte(w) }
+ b[5] = 1
+ push_execstack(b)
+ while curstack < execstackptr do
+ do_exec()
+ end
+ local entry = execstack[execstackptr]
+ if entry[1] == '.exit' and entry[4] == true then
+ pop_execstack()
+ return true;
+ end
+ do_show(fontdict,w)
+ v = w
+ end
+ return true
+end
+
+local the_standardencoding = {
+ '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
+ '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
+ '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
+ '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
+ '', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand',
+ 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma',
+ 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four',
+ 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less',
+ 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F',
+ 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
+ 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b',
+ 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+ 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar',
+ 'braceright', 'asciitilde', '.notdef', '.notdef', '.notdef',
+ '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
+ '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
+ '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
+ '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
+ '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
+ '.notdef', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen',
+ 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft',
+ 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl',
+ '.notdef', 'endash', 'dagger', 'daggerdbl', 'periodcentered', '.notdef',
+ 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase',
+ 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', '.notdef',
+ 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron',
+ 'breve', 'dotaccent', 'dieresis', '.notdef', 'ring', 'cedilla',
+ '.notdef', 'hungarumlaut', 'ogonek', 'caron', 'emdash', '.notdef',
+ '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
+ '.notdef', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
+ '.notdef', '.notdef', '.notdef', 'AE', '.notdef', 'ordfeminine',
+ '.notdef', '.notdef', '.notdef', '.notdef', 'Lslash', 'Oslash', 'OE',
+ 'ordmasculine', '.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
+ 'ae', '.notdef', '.notdef', '.notdef', 'dotlessi', '.notdef',
+ '.notdef', 'lslash', 'oslash', 'oe', 'germandbls', '.notdef',
+ '.notdef', '.notdef', '.notdef'
+}
+
+local function standardencoding()
+ local a = { }
+ for i=1,#the_standardencoding do
+ a[i] = { 'name', 'unlimited', 'literal', add_VM(the_standardencoding[i]) }
+ end
+ return a
+end
+
+-- Font cache operators
+--
+-- -cachestatus -setcachedevice -setcharwidth -setcachelimit
+
+-- userdict (initially empty)
+
+local systemdict
+local userdict
+
+initializers[#initializers+1] = function(reset)
+ if reset then
+ systemdict = nil
+ else
+ dictstackptr = dictstackptr + 1
+ dictstack[dictstackptr] = add_VM {
+ access = 'unlimited',
+ maxsize = MAX_INTEGER,
+ size = 0,
+ dict = { },
+ }
+ if directvm then
+ systemdict = dictstack[dictstackptr]
+ else
+ systemdict = dictstackptr
+ end
+ end
+end
+
+initializers[#initializers+1] = function(reset)
+ if reset then
+ userdict = nil
+ else
+ dictstackptr = dictstackptr + 1
+ dictstack[dictstackptr] = add_VM {
+ access = 'unlimited',
+ maxsize = MAX_INTEGER,
+ size = 0,
+ dict = { },
+ }
+ if directvm then
+ userdict = dictstack[dictstackptr]
+ else
+ userdict = dictstackptr
+ end
+ end
+end
+
+initializers[#initializers+1] = function(reset)
+ if reset then
+ -- already done
+ else
+ local dict = {
+ ['$error'] = { 'dict', 'unlimited', 'literal', dicterror },
+ ['['] = { 'operator', 'unlimited', 'executable', operators.beginarray, '[' },
+ [']'] = { 'operator', 'unlimited', 'executable', operators.endarray, ']' },
+ -- ['='] = { 'operator', 'unlimited', 'executable', operators.EQ, '=' },
+ ['=='] = { 'operator', 'unlimited', 'executable', operators.equal, '==' },
+ ['abs'] = { 'operator', 'unlimited', 'executable', operators.abs, 'abs' },
+ ['add'] = { 'operator', 'unlimited', 'executable', operators.add, 'add' },
+ ['aload'] = { 'operator', 'unlimited', 'executable', operators.aload, 'aload' },
+ ['anchorsearch'] = { 'operator', 'unlimited', 'executable', operators.anchorsearch, 'anchorsearch' },
+ ['and'] = { 'operator', 'unlimited', 'executable', operators["and"], 'and' },
+ ['arc'] = { 'operator', 'unlimited', 'executable', operators.arc, 'arc' },
+ ['arcn'] = { 'operator', 'unlimited', 'executable', operators.arcn, 'arcn' },
+ ['arcto'] = { 'operator', 'unlimited', 'executable', operators.arcto, 'arcto' },
+ ['array'] = { 'operator', 'unlimited', 'executable', operators.array, 'array' },
+ ['astore'] = { 'operator', 'unlimited', 'executable', operators.astore, 'astore' },
+ ['atan'] = { 'operator', 'unlimited', 'executable', operators.atan, 'atan' },
+ ['banddevice'] = { 'operator', 'unlimited', 'executable', operators.banddevice, 'banddevice' },
+ ['bind'] = { 'operator', 'unlimited', 'executable', operators.bind, 'bind' },
+ ['bitshift'] = { 'operator', 'unlimited', 'executable', operators.bitshift, 'bitshift' },
+ ['begin'] = { 'operator', 'unlimited', 'executable', operators.begin, 'begin' },
+ ['bytesavailable'] = { 'operator', 'unlimited', 'executable', operators.bytesavailable, 'bytesavailable' },
+ ['ceiling'] = { 'operator', 'unlimited', 'executable', operators.ceiling, 'ceiling' },
+ ['clear'] = { 'operator', 'unlimited', 'executable', operators.clear, 'clear' },
+ ['cleartomark'] = { 'operator', 'unlimited', 'executable', operators.cleartomark, 'cleartomark' },
+ ['clip'] = { 'operator', 'unlimited', 'executable', operators.clip, 'clip' },
+ ['clippath'] = { 'operator', 'unlimited', 'executable', operators.clippath, 'clippath' },
+ ['closefile'] = { 'operator', 'unlimited', 'executable', operators.closefile, 'closefile' },
+ ['closepath'] = { 'operator', 'unlimited', 'executable', operators.closepath, 'closepath' },
+ ['concat'] = { 'operator', 'unlimited', 'executable', operators.concat, 'concat' },
+ ['concatmatrix'] = { 'operator', 'unlimited', 'executable', operators.concatmatrix, 'concatmatrix' },
+ ['copy'] = { 'operator', 'unlimited', 'executable', operators.copy, 'copy' },
+ ['copypage'] = { 'operator', 'unlimited', 'executable', operators.copypage, 'copypage' },
+ ['cos'] = { 'operator', 'unlimited', 'executable', operators.cos, 'cos' },
+ ['count'] = { 'operator', 'unlimited', 'executable', operators.count, 'count' },
+ ['countdictstack'] = { 'operator', 'unlimited', 'executable', operators.countdictstack, 'countdictstack' },
+ ['countexecstack'] = { 'operator', 'unlimited', 'executable', operators.countexecstack, 'countexecstack' },
+ ['counttomark'] = { 'operator', 'unlimited', 'executable', operators.counttomark, 'counttomark' },
+ ['currentdash'] = { 'operator', 'unlimited', 'executable', operators.currentdash, 'currentdash' },
+ ['currentdict'] = { 'operator', 'unlimited', 'executable', operators.currentdict, 'currentdict' },
+ ['currentfile'] = { 'operator', 'unlimited', 'executable', operators.currentfile, 'currentfile' },
+ ['currentflat'] = { 'operator', 'unlimited', 'executable', operators.currentflat, 'currentflat' },
+ ['currentfont'] = { 'operator', 'unlimited', 'executable', operators.currentfont, 'currentfont' },
+ ['currentgray'] = { 'operator', 'unlimited', 'executable', operators.currentgray, 'currentgray' },
+ ['currenthsbcolor'] = { 'operator', 'unlimited', 'executable', operators.currenthsbcolor, 'currenthsbcolor' },
+ ['currentlinecap'] = { 'operator', 'unlimited', 'executable', operators.currentlinecap, 'currentlinecap' },
+ ['currentlinejoin'] = { 'operator', 'unlimited', 'executable', operators.currentlinejoin, 'currentlinejoin' },
+ ['currentlinewidth'] = { 'operator', 'unlimited', 'executable', operators.currentlinewidth, 'currentlinewidth' },
+ ['currentmatrix'] = { 'operator', 'unlimited', 'executable', operators.currentmatrix, 'currentmatrix' },
+ ['currentmiterlimit'] = { 'operator', 'unlimited', 'executable', operators.currentmiterlimit, 'currentmiterlimit' },
+ ['currentpoint'] = { 'operator', 'unlimited', 'executable', operators.currentpoint, 'currentpoint' },
+ ['currentrgbcolor'] = { 'operator', 'unlimited', 'executable', operators.currentrgbcolor, 'currentrgbcolor' },
+ ['currentcmykcolor'] = { 'operator', 'unlimited', 'executable', operators.currentcmykcolor, 'currentcmykcolor' },
+ ['currentscreen'] = { 'operator', 'unlimited', 'executable', operators.currentscreen, 'currentscreen' },
+ ['currenttransfer'] = { 'operator', 'unlimited', 'executable', operators.currenttransfer, 'currenttransfer' },
+ ['curveto'] = { 'operator', 'unlimited', 'executable', operators.curveto, 'curveto' },
+ ['cvi'] = { 'operator', 'unlimited', 'executable', operators.cvi, 'cvi' },
+ ['cvlit'] = { 'operator', 'unlimited', 'executable', operators.cvlit, 'cvlit' },
+ ['cvn'] = { 'operator', 'unlimited', 'executable', operators.cvn, 'cvn' },
+ ['cvr'] = { 'operator', 'unlimited', 'executable', operators.cvr, 'cvr' },
+ ['cvrs'] = { 'operator', 'unlimited', 'executable', operators.cvrs, 'cvrs' },
+ ['cvs'] = { 'operator', 'unlimited', 'executable', operators.cvs, 'cvs' },
+ ['cvx'] = { 'operator', 'unlimited', 'executable', operators.cvx, 'cvx' },
+ ['def'] = { 'operator', 'unlimited', 'executable', operators.def, 'def' },
+ ['definefont'] = { 'operator', 'unlimited', 'executable', operators.definefont, 'definefont' },
+ ['dict'] = { 'operator', 'unlimited', 'executable', operators.dict, 'dict' },
+ ['dictstack'] = { 'operator', 'unlimited', 'executable', operators.dictstack, 'dictstack' },
+ ['div'] = { 'operator', 'unlimited', 'executable', operators.div, 'div' },
+ ['dtransform'] = { 'operator', 'unlimited', 'executable', operators.dtransform, 'dtransform' },
+ ['dup'] = { 'operator', 'unlimited', 'executable', operators.dup, 'dup' },
+ ['echo'] = { 'operator', 'unlimited', 'executable', operators.echo, 'echo' },
+ ['end'] = { 'operator', 'unlimited', 'executable', operators["end"], 'end' },
+ ['eoclip'] = { 'operator', 'unlimited', 'executable', operators.eoclip, 'eoclip' },
+ ['eofill'] = { 'operator', 'unlimited', 'executable', operators.eofill, 'eofill' },
+ ['eq'] = { 'operator', 'unlimited', 'executable', operators.eq, 'eq' },
+ ['errordict'] = { 'dict', 'unlimited', 'literal', errordict },
+ ['exch'] = { 'operator', 'unlimited', 'executable', operators.exch, 'exch' },
+ ['exec'] = { 'operator', 'unlimited', 'executable', operators.exec, 'exec' },
+ ['execstack'] = { 'operator', 'unlimited', 'executable', operators.execstack, 'execstack' },
+ ['executeonly'] = { 'operator', 'unlimited', 'executable', operators.executeonly, 'executeonly' },
+ ['exit'] = { 'operator', 'unlimited', 'executable', operators.exit, 'exit' },
+ ['exp'] = { 'operator', 'unlimited', 'executable', operators.exp, 'exp' },
+ ['false'] = { 'boolean', 'unlimited', 'literal', false },
+ ['file'] = { 'operator', 'unlimited', 'executable', operators.file, 'file' },
+ ['fill'] = { 'operator', 'unlimited', 'executable', operators.fill, 'fill' },
+ ['findfont'] = { 'operator', 'unlimited', 'executable', operators.findfont, 'findfont' },
+ ['FontDirectory'] = { 'dict', 'unlimited', 'literal', escrito['FontDirectory'] },
+ ['flattenpath'] = { 'operator', 'unlimited', 'executable', operators.flattenpath, 'flattenpath' },
+ ['floor'] = { 'operator', 'unlimited', 'executable', operators.floor, 'floor' },
+ ['flush'] = { 'operator', 'unlimited', 'executable', operators.flush, 'flush' },
+ ['flushfile'] = { 'operator', 'unlimited', 'executable', operators.flushfile, 'flushfile' },
+ ['for'] = { 'operator', 'unlimited', 'executable', operators["for"], 'for' },
+ ['forall'] = { 'operator', 'unlimited', 'executable', operators.forall, 'forall' },
+ ['framedevice'] = { 'operator', 'unlimited', 'executable', operators.framedevice, 'framedevice' },
+ ['ge'] = { 'operator', 'unlimited', 'executable', operators.ge, 'ge' },
+ ['get'] = { 'operator', 'unlimited', 'executable', operators.get, 'get' },
+ ['getinterval'] = { 'operator', 'unlimited', 'executable', operators.getinterval, 'getinterval' },
+ ['grestore'] = { 'operator', 'unlimited', 'executable', operators.grestore, 'grestore' },
+ ['grestoreall'] = { 'operator', 'unlimited', 'executable', operators.grestoreall, 'grestoreall' },
+ ['gsave'] = { 'operator', 'unlimited', 'executable', operators.gsave, 'gsave' },
+ ['gt'] = { 'operator', 'unlimited', 'executable', operators.gt, 'gt' },
+ ['identmatrix'] = { 'operator', 'unlimited', 'executable', operators.identmatrix, 'identmatrix' },
+ ['idiv'] = { 'operator', 'unlimited', 'executable', operators.idiv, 'idiv' },
+ ['if'] = { 'operator', 'unlimited', 'executable', operators["if"], 'if' },
+ ['ifelse'] = { 'operator', 'unlimited', 'executable', operators.ifelse, 'ifelse' },
+ ['index'] = { 'operator', 'unlimited', 'executable', operators.index, 'index' },
+ ['initclip'] = { 'operator', 'unlimited', 'executable', operators.initclip, 'initclip' },
+ ['initgraphics'] = { 'operator', 'unlimited', 'executable', operators.initgraphics, 'initgraphics' },
+ ['initmatrix'] = { 'operator', 'unlimited', 'executable', operators.initmatrix, 'initmatrix' },
+ ['invertmatrix'] = { 'operator', 'unlimited', 'executable', operators.invertmatrix, 'invertmatrix' },
+ ['idtransform'] = { 'operator', 'unlimited', 'executable', operators.idtransform, 'idtransform' },
+ ['itransform'] = { 'operator', 'unlimited', 'executable', operators.itransform, 'itransform' },
+ ['known'] = { 'operator', 'unlimited', 'executable', operators.known, 'known' },
+ ['kshow'] = { 'operator', 'unlimited', 'executable', operators.kshow, 'kshow' },
+ ['le'] = { 'operator', 'unlimited', 'executable', operators.le, 'le' },
+ ['length'] = { 'operator', 'unlimited', 'executable', operators.length, 'length' },
+ ['lineto'] = { 'operator', 'unlimited', 'executable', operators.lineto, 'lineto' },
+ ['ln'] = { 'operator', 'unlimited', 'executable', operators.ln, 'ln' },
+ ['load'] = { 'operator', 'unlimited', 'executable', operators.load, 'load' },
+ ['log'] = { 'operator', 'unlimited', 'executable', operators.log, 'log' },
+ ['loop'] = { 'operator', 'unlimited', 'executable', operators.loop, 'loop' },
+ ['lt'] = { 'operator', 'unlimited', 'executable', operators.lt, 'lt' },
+ ['makefont'] = { 'operator', 'unlimited', 'executable', operators.makefont, 'makefont' },
+ ['mark'] = { 'operator', 'unlimited', 'executable', operators.mark, 'mark' },
+ ['matrix'] = { 'operator', 'unlimited', 'executable', operators.matrix, 'matrix' },
+ ['maxlength'] = { 'operator', 'unlimited', 'executable', operators.maxlength, 'maxlength' },
+ ['mod'] = { 'operator', 'unlimited', 'executable', operators.mod, 'mod' },
+ ['moveto'] = { 'operator', 'unlimited', 'executable', operators.moveto, 'moveto' },
+ ['mul'] = { 'operator', 'unlimited', 'executable', operators.mul, 'mul' },
+ ['ne'] = { 'operator', 'unlimited', 'executable', operators.ne, 'ne' },
+ ['neg'] = { 'operator', 'unlimited', 'executable', operators.neg, 'neg' },
+ ['newpath'] = { 'operator', 'unlimited', 'executable', operators.newpath, 'newpath' },
+ ['noaccess'] = { 'operator', 'unlimited', 'executable', operators.noaccess, 'noaccess' },
+ ['not'] = { 'operator', 'unlimited', 'executable', operators["not"], 'not' },
+ ['null'] = { 'operator', 'unlimited', 'executable', operators.null, 'null' },
+ ['or'] = { 'operator', 'unlimited', 'executable', operators["or"], 'or' },
+ ['pop'] = { 'operator', 'unlimited', 'executable', operators.pop, 'pop' },
+ ['print'] = { 'operator', 'unlimited', 'executable', operators.print, 'print' },
+ ['pstack'] = { 'operator', 'unlimited', 'executable', operators.pstack, 'pstack' },
+ ['put'] = { 'operator', 'unlimited', 'executable', operators.put, 'put' },
+ ['putinterval'] = { 'operator', 'unlimited', 'executable', operators.putinterval, 'putinterval' },
+ ['quit'] = { 'operator', 'unlimited', 'executable', operators.quit, 'quit' },
+ ['rand'] = { 'operator', 'unlimited', 'executable', operators.rand, 'rand' },
+ ['rcheck'] = { 'operator', 'unlimited', 'executable', operators.rcheck, 'rcheck' },
+ ['rcurveto'] = { 'operator', 'unlimited', 'executable', operators.rcurveto, 'rcurveto' },
+ ['read'] = { 'operator', 'unlimited', 'executable', operators.read, 'read' },
+ ['readhexstring'] = { 'operator', 'unlimited', 'executable', operators.readhexstring, 'readhexstring' },
+ ['readline'] = { 'operator', 'unlimited', 'executable', operators.readline, 'readline' },
+ ['readonly'] = { 'operator', 'unlimited', 'executable', operators.readonly, 'readonly' },
+ ['renderbands'] = { 'operator', 'unlimited', 'executable', operators.renderbands, 'renderbands' },
+ ['repeat'] = { 'operator', 'unlimited', 'executable', operators["repeat"], 'repeat' },
+ ['resetfile'] = { 'operator', 'unlimited', 'executable', operators.resetfile, 'resetfile' },
+ ['restore'] = { 'operator', 'unlimited', 'executable', operators.restore, 'restore' },
+ ['rlineto'] = { 'operator', 'unlimited', 'executable', operators.rlineto, 'rlineto' },
+ ['rmoveto'] = { 'operator', 'unlimited', 'executable', operators.rmoveto, 'rmoveto' },
+ ['roll'] = { 'operator', 'unlimited', 'executable', operators.roll, 'roll' },
+ ['rotate'] = { 'operator', 'unlimited', 'executable', operators.rotate, 'rotate' },
+ ['round'] = { 'operator', 'unlimited', 'executable', operators.round, 'round' },
+ ['rrand'] = { 'operator', 'unlimited', 'executable', operators.rrand, 'rrand' },
+ ['run'] = { 'operator', 'unlimited', 'executable', operators.run, 'run' },
+ ['save'] = { 'operator', 'unlimited', 'executable', operators.save, 'save' },
+ ['scale'] = { 'operator', 'unlimited', 'executable', operators.scale, 'scale' },
+ ['scalefont'] = { 'operator', 'unlimited', 'executable', operators.scalefont, 'scalefont' },
+ ['search'] = { 'operator', 'unlimited', 'executable', operators.search, 'search' },
+ ['setdash'] = { 'operator', 'unlimited', 'executable', operators.setdash, 'setdash' },
+ ['setflat'] = { 'operator', 'unlimited', 'executable', operators.setflat, 'setflat' },
+ ['setfont'] = { 'operator', 'unlimited', 'executable', operators.setfont, 'setfont' },
+ ['setgray'] = { 'operator', 'unlimited', 'executable', operators.setgray, 'setgray' },
+ ['sethsbcolor'] = { 'operator', 'unlimited', 'executable', operators.sethsbcolor, 'sethsbcolor' },
+ ['setlinecap'] = { 'operator', 'unlimited', 'executable', operators.setlinecap, 'setlinecap' },
+ ['setlinejoin'] = { 'operator', 'unlimited', 'executable', operators.setlinejoin, 'setlinejoin' },
+ ['setlinewidth'] = { 'operator', 'unlimited', 'executable', operators.setlinewidth, 'setlinewidth' },
+ ['setmatrix'] = { 'operator', 'unlimited', 'executable', operators.setmatrix, 'setmatrix' },
+ ['setmiterlimit'] = { 'operator', 'unlimited', 'executable', operators.setmiterlimit, 'setmiterlimit' },
+ ['setrgbcolor'] = { 'operator', 'unlimited', 'executable', operators.setrgbcolor, 'setrgbcolor' },
+ ['setcmykcolor'] = { 'operator', 'unlimited', 'executable', operators.setcmykcolor, 'setcmykcolor' },
+ ['setscreen'] = { 'operator', 'unlimited', 'executable', operators.setscreen, 'setscreen' },
+ ['settransfer'] = { 'operator', 'unlimited', 'executable', operators.settransfer, 'settransfer' },
+ ['show'] = { 'operator', 'unlimited', 'executable', operators.show, 'show' },
+ ['showpage'] = { 'operator', 'unlimited', 'executable', operators.showpage, 'showpage' },
+ ['sin'] = { 'operator', 'unlimited', 'executable', operators.sin, 'sin' },
+ ['sqrt'] = { 'operator', 'unlimited', 'executable', operators.sqrt, 'sqrt' },
+ ['srand'] = { 'operator', 'unlimited', 'executable', operators.srand, 'srand' },
+ ['stack'] = { 'operator', 'unlimited', 'executable', operators.stack, 'stack' },
+ ['start'] = { 'operator', 'unlimited', 'executable', operators.start, 'start' },
+ ['StandardEncoding'] = { 'array', 'unlimited', 'literal', add_VM(standardencoding()), 256, 256 },
+ ['status'] = { 'operator', 'unlimited', 'executable', operators.status, 'status' },
+ ['stop'] = { 'operator', 'unlimited', 'executable', operators.stop, 'stop' },
+ ['stopped'] = { 'operator', 'unlimited', 'executable', operators.stopped, 'stopped' },
+ ['store'] = { 'operator', 'unlimited', 'executable', operators.store, 'store' },
+ ['string'] = { 'operator', 'unlimited', 'executable', operators.string, 'string' },
+ ['stroke'] = { 'operator', 'unlimited', 'executable', operators.stroke, 'stroke' },
+ ['sub'] = { 'operator', 'unlimited', 'executable', operators.sub, 'sub' },
+ ['systemdict'] = { 'dict', 'unlimited', 'literal', systemdict },
+ ['token'] = { 'operator', 'unlimited', 'executable', operators.token, 'token' },
+ ['translate'] = { 'operator', 'unlimited', 'executable', operators.translate, 'translate' },
+ ['transform'] = { 'operator', 'unlimited', 'executable', operators.transform, 'transform' },
+ ['true'] = { 'boolean', 'unlimited', 'literal', true },
+ ['truncate'] = { 'operator', 'unlimited', 'executable', operators.truncate, 'truncate' },
+ ['type'] = { 'operator', 'unlimited', 'executable', operators.type, 'type' },
+ ['userdict'] = { 'dict', 'unlimited', 'literal', userdict },
+ ['usertime'] = { 'operator', 'unlimited', 'executable', operators.usertime, 'usertime' },
+ ['version'] = { 'operator', 'unlimited', 'executable', operators.version, 'version' },
+ ['vmstatus'] = { 'operator', 'unlimited', 'executable', operators.vmstatus, 'vmstatus' },
+ ['wcheck'] = { 'operator', 'unlimited', 'executable', operators.wcheck, 'wcheck' },
+ ['where'] = { 'operator', 'unlimited', 'executable', operators.where, 'where' },
+ ['write'] = { 'operator', 'unlimited', 'executable', operators.write, 'write' },
+ ['writehexstring'] = { 'operator', 'unlimited', 'executable', operators.writehexstring, 'writehexstring' },
+ ['writestring'] = { 'operator', 'unlimited', 'executable', operators.writestring, 'writestring' },
+ ['xcheck'] = { 'operator', 'unlimited', 'executable', operators.xcheck, 'xcheck' },
+ ['xor'] = { 'operator', 'unlimited', 'executable', operators.xor, 'xor' },
+ }
+ if directvm then
+ systemdict.dict = dict
+ else
+ VM[dictstack[systemdict]].dict = dict
+ end
+ end
+end
+
+initializers[#initializers+1] = function(reset)
+ if reset then
+ dicterror = nil
+ errordict = nil
+ else
+ dicterror = add_VM {
+ access = 'unlimited',
+ size = 1,
+ maxsize = 40,
+ dict = {
+ newerror = { 'boolean', 'unlimited', 'literal', false }
+ },
+ }
+ --
+ errordict = add_VM {
+ access = 'unlimited',
+ size = 0,
+ maxsize = 40,
+ dict = { },
+ }
+ --
+ local d
+ if directvm then
+ d = systemdict.dict
+ else
+ d = VM[dictstack[systemdict]].dict
+ end
+ -- still needed ?
+ d['errordict'] = { 'dict', 'unlimited', 'literal', errordict }
+ d['systemdict'] = { 'dict', 'unlimited', 'literal', systemdict }
+ d['userdict'] = { 'dict', 'unlimited', 'literal', userdict }
+ d['$error'] = { 'dict', 'unlimited', 'literal', dicterror }
+ end
+end
+
+-- What follows is the main interpreter, with the tokenizer first
+
+-- procedure scanning stack for the tokenizer
+
+local procstack
+local procstackptr
+
+initializers[#initializers+1] = function(reset)
+ if reset then
+ procstack = nil
+ procstackptr = nil
+ else
+ procstack = { }
+ procstackptr = 0
+ end
+end
+
+-- lpeg parser for tokenization
+
+do
+
+ local function push(v)
+ if procstackptr > 0 then
+ local top = procstack[procstackptr]
+ if top then
+ top[#top+1] = v
+ else
+ procstack[procstackptr] = { v }
+ end
+ return false
+ else
+ push_execstack(v)
+ return true
+ end
+ end
+
+ local function start()
+ procstackptr = procstackptr + 1
+ return true
+ end
+
+ local function stop()
+ local v = procstack[procstackptr]
+ procstack[procstackptr] = { }
+ procstackptr = procstackptr - 1
+ if push {'array', 'unlimited', 'executable', add_VM(v), 1, #v, 'd' } then
+ return true
+ end
+ end
+
+ local function hexify(a)
+ return char(tonumber(a,16))
+ end
+
+ local function octify(a)
+ return char(tonumber(a,8))
+ end
+
+ local function radixed(base,value)
+ base = tonumber(base)
+ if base > 36 or base < 2 then
+ return nil
+ end
+ value = tonumber(value,base)
+ if not value then
+ return "error", false
+ elseif value > MAX_INT then
+ return "integer", value
+ else
+ return "real", value
+ end
+ end
+
+ local space = S(' ')
+ local spacing = S(' \t\r\n\f')
+ local sign = S('+-')^-1
+ local digit = R('09')
+ local period = P('.')
+ local letters = R('!~') - S('[]<>{}()%')
+ local hexdigit = R('09','af','AF')
+ local radixdigit = R('09','az','AZ')
+
+ local p_integer = (sign * digit^1 * #(1-letters)) / tonumber
+ local p_real = ((sign * digit^0 * period * digit^0 + period * digit^1) * (S('eE') * sign * digit^1)^-1 * #(1-letters)) / tonumber
+ local p_literal = Cs(P("/")/"" * letters^1 * letters^0)
+ local p_symbol = C(letters^1 * letters^0)
+ ----- p_radixed = C(digit^1) * P("#") * C(radixdigit^1) * #(1-letters) / radixed-- weird #() here
+ local p_radixed = C(digit^1) * P("#") * C(radixdigit^1) / radixed
+ local p_unhexed = P("<") * Cs(((C(hexdigit*hexdigit) * Cc(16))/tonumber/char+spacing/"")^0) * P(">")
+ local p_comment = P('%') * (1 - S('\r\n'))^0 * Cc(true)
+ local p_bounding = P('%%BoundingBox:') * Ct((space^0 * p_integer)^4) * (1 - S('\r\n'))^0
+ local p_lbrace = C("{")
+ local p_rbrace = C("}")
+ local p_lbracket = C("[")
+ local p_rbracket = C("]")
+ local p_finish = Cc(false)
+
+ local p_string =
+ P("(")
+ * Cs( P {
+ (
+ (1 - S("()\\"))^1
+ + P("\\")/"" * (
+ (C(digit *digit * digit) * Cc(8)) / tonumber / char
+ + P("n") / "\n" + P("r") / "\r" + P("t") / "\t"
+ + P("b") / "\b" + P("f") / "\f" + P("\\") / "\\"
+ + 1
+ )
+ + P("(") * V(1) * P(")")
+ )^0
+ })
+ * P(")")
+
+ -- inspect(lpegmatch(p_radixed,"10#123"))
+ -- inspect(lpegmatch(p_unhexed,"<A2B3 C3>"))
+ -- inspect(lpegmatch(p_string,[[(foo(bar \124\125 \( bar\n bar\\bar))]]))
+
+ local p_unhexed = Cc('string') * p_unhexed
+ local p_string = Cc('string') * p_string
+ local p_array_start = Cc('name') * p_lbracket
+ local p_array_stop = Cc('name') * p_rbracket
+ local p_exec_start = Cc('start') * p_lbrace
+ local p_exec_stop = Cc('stop') * p_rbrace
+ local p_integer = Cc('integer') * p_integer
+ local p_real = Cc('real') * p_real
+ local p_radixed = p_radixed
+ local p_symbol = Cc('name') * p_symbol
+ local p_literal = Cc('literal') * p_literal
+ local p_comment = Cc('comment') * p_comment
+ local p_bounding = Cc('bounding') * p_bounding
+ local p_finish = Cc("eof") * p_finish
+ local p_whitespace = spacing^0
+
+ local tokens = p_whitespace
+ * (
+ p_bounding
+ + p_comment
+ + p_string
+ + p_unhexed
+ + p_array_start
+ + p_array_stop
+ + p_exec_start
+ + p_exec_stop
+ + p_real
+ + p_radixed
+ + p_integer
+ + p_literal
+ + p_symbol
+ + p_finish
+ )^-1
+ * Cp()
+
+ -- we can do push etc in the lpeg but the call is not faster than the check
+ -- and this stays closer to the original
+
+ local function tokenize()
+ local object = execstack[execstackptr]
+ local sequence = object[4]
+ local position = object[5]
+ local length = object[6]
+ local tokentype = nil
+ local value = nil
+ while position < length do
+ tokentype, value, position = lpegmatch(tokens,get_VM(sequence),position)
+ if not position then
+ return false
+ elseif position >= length then
+ pop_execstack()
+ else
+ object[5] = position
+ end
+ if not value then
+ return false -- handle_error('syntaxerror')
+ elseif tokentype == 'integer' or tokentype == 'real' then
+ if push { tokentype, 'unlimited', 'literal', value } then
+ return true
+ end
+ elseif tokentype == 'name' then
+ if push { 'name', 'unlimited', 'executable', add_VM(value) } then
+ return true
+ end
+ elseif tokentype == 'literal' then
+ if push { 'name', 'unlimited', 'literal', add_VM(value) } then
+ return true
+ end
+ elseif tokentype == 'string' then
+ if push { 'string', 'unlimited', 'literal', add_VM(value), 1, #value } then
+ return true
+ end
+ elseif tokentype == 'start' then
+ if start() then
+ -- stay
+ end
+ elseif tokentype == 'stop' then
+ if stop() then
+ return true
+ end
+ elseif tokentype == 'bounding' then
+ specials.boundingbox = value
+ end
+ end
+ return position >= length
+ end
+
+ -- the exec stack can contain a limited amount of interesting item types
+ -- to be handled by next_object:
+ -- executable arrays (procedures)
+ -- executable strings
+ -- executable files
+
+ next_object = function()
+ if execstackptr == 0 then
+ return nil
+ end
+ local object = execstack[execstackptr]
+ if not object then
+ return nil
+ end
+ local otyp = object[1]
+ local exec = object[3] == 'executable'
+ if not exec then
+ return pop_execstack()
+ elseif otyp == 'array' then
+ if object[7] == 'd' then
+ return pop_execstack()
+ else
+ local proc = get_VM(object[4])
+ local o = object[5]
+ local val = proc[o]
+ if o >= #proc then
+ object[5] = 1
+ pop_execstack()
+ else
+ object[5] = o + 1
+ end
+ return val
+ end
+ elseif otyp == 'string' then
+ if not tokenize() then
+ report("tokenizer failed on string")
+ return nil
+ else
+ return next_object() -- recurse
+ end
+ elseif otyp == 'file' then
+ if object[4] == 0 then
+ report('sorry, interactive mode is not supported')
+ end
+ if not tokenize() then
+ report("tokenizer failed on file")
+ return nil
+ else
+ return next_object() -- recurse
+ end
+ else
+ return pop_execstack()
+ end
+ end
+
+-- The main execution control function
+
+ local detail = false -- much faster
+
+ local report_exec = logs.reporter("escrito","exec")
+
+ do_exec = function() -- already a local
+ local ret
+ local savedopstack = detail and copy_opstack()
+ local object = next_object()
+ if not object then
+ return false
+ end
+ local otyp = object[1]
+ if DEBUG then
+ if otyp == 'operator' then
+ report_exec("%s %s %s",otyp,object[3],object[5])
+ elseif otyp == 'dict' then
+ local d = get_VM(object[4])
+ report_exec("%s %s <%s:%s>",otyp,object[3],d.size or '',d.maxsize or '')
+ elseif otyp == 'array' or otyp == 'file' or otyp == 'save' then
+ report_exec("%s <%s:%s>",object[3],object[5] or '',object[6] or '')
+ elseif otyp == 'string' or otyp == 'name' then
+ report_exec("%s %s %s",otyp,object[3],get_VM(object[4]))
+ else
+ report_exec("%s %s %s",otyp,object[3],tostring(object[4]))
+ end
+ end
+ if otyp == 'real' or otyp == 'integer' or otyp == 'boolean' or otyp == 'mark' or otyp == 'save' or otyp == 'font' then
+ push_opstack(object)
+ elseif otyp == '.stopped' then
+ -- when .stopped is seen here, stop was never called
+ push_opstack { 'boolean', 'unlimited', 'executable', false}
+ elseif otyp == '.exit' then
+ -- when .exit is seen here, exit was never called
+ elseif otyp == 'array' then
+ if object[2] == 'noaccess' then
+ escrito.errorname = 'noaccess'
+ else
+ push_opstack(object)
+ end
+ elseif otyp == 'string' then
+ if object[2] == 'noaccess' then
+ escrito.errorname = 'noaccess'
+ else
+ push_opstack(object)
+ end
+ elseif otyp == 'dict' then
+ local dict = get_VM(object[4])
+ if dict.access == 'noaccess' then
+ escrito.errorname = 'noaccess'
+ else
+ push_opstack(object)
+ end
+ elseif otyp == 'file' then
+ if object[2] == 'noaccess' then
+ errorname = 'noaccess'
+ else
+ push_opstack(object)
+ end
+ elseif otyp == 'null' then
+ push_opstack(object)
+ elseif otyp == 'operator' then
+ if object[3]=='executable' then
+ ret, escrito.errorname = object[4]()
+ else
+ push_opstack(object)
+ end
+ elseif otyp == 'save' then
+ -- todo
+ elseif otyp == 'name' then
+ if object[3] == 'executable' then
+ local v = lookup(get_VM(object[4]))
+ if not v then
+ if escrito.errorname then
+ -- doesn't work, needs thinking
+ error ("recursive error detected inside '" .. escrito.errorname .. "'")
+ end
+ escrito.errorname = 'undefined'
+ else
+ if DEBUG then
+ local vt = v[1]
+ if vt == 'operator' then
+ print ('exec2: ' .. vt .. ' ' .. v[3] .. ' '.. v[5])
+ elseif vt == 'dict' or vt == 'array' or vt == 'file' or vt == 'save' then
+ print ('exec2: ' .. vt .. ' ' .. v[3] .. ' <'.. (v[5] or '') .. '>')
+ elseif vt == 'string' or vt == 'name' then
+ print ('exec2: ' .. vt .. ' ' .. v[3] .. ' '.. get_VM(v[4]))
+ else
+ print ('exec2: ' .. vt .. ' ' .. v[3] .. ' '.. tostring(v[4]))
+ end
+ end
+ push_execstack(v)
+ end
+ else
+ push_opstack(object)
+ end
+ elseif otyp == 'null' then
+ -- do nothing
+ elseif otyp == 'array' then
+ push_opstack(object)
+ end
+
+ if escrito.errorname then
+ if savedopstack then
+ local v = lookup_error(escrito.errorname)
+ if not v then
+ print("unknown error handler for '" .. escrito.errorname .. "', quitting")
+ return false
+ else
+ set_opstack(savedopstack)
+ push_opstack { otyp, object[2], "literal", object[4], object[5], object[6], object[7] }
+ push_opstack { 'string','unlimited','literal',add_VM(escrito.errorname), 1 }
+ push_execstack(v)
+ end
+ escrito.errorname = nil
+ else
+ print("error '" .. escrito.errorname .. "', quitting")
+ -- os.exit()
+ end
+ end
+
+ return true
+ end
+
+end
+
+do
+
+ -- some of the errors will never actually happen
+
+ local errornames = {
+ "dictfull", "dictstackoverflow", "dictstackunderflow", "execstackoverflow",
+ "interrupt", "invalidaccess", "invalidexit", "invalidfileaccess", "invalidfont", "invalidrestore",
+ "ioerror", "limitcheck", "nocurrentpoint", "rangecheck", "stackoverflow", "stackunderflow",
+ "syntaxerror", "timeout", "typecheck", "undefined", "undefinedfilename", "undefinedresult",
+ "unmatchedmark", "unregistered", "VMerror"
+ }
+
+ local generic_error_proc = [[{
+ $error /newerror true put
+ $error exch /errorname exch put
+ $error exch /command exch put
+ count array astore $error /ostack 3 -1 roll put
+ $error /dstack countdictstack array dictstack put
+ countexecstack array execstack aload pop pop count array astore $error /estack 3 -1 roll put
+ stop
+ } bind ]]
+
+ local generic_handleerror_proc = [[{
+ $error begin
+ /newerror false def
+ (%%[ Error: ) print
+ errorname print
+ (; OffendingCommand: ) print
+ command ==
+ ( ]%%\n) print flush
+ end
+ }]]
+
+ local enabled
+
+ local function interpret(data)
+ if enabled then
+ push_opstack { 'file', 'unlimited', 'executable', add_VM(data), 1, #data, 'r', stdin }
+ push_execstack { 'operator', 'unlimited', 'executable', operators.stopped, 'stopped' }
+ while true do
+ if not do_exec() then
+ local v = pop_opstack()
+ if v and v[4] == true then
+ local proc = {
+ { 'name', 'unlimited', 'executable', add_VM('errordict') }, -- hm, errordict
+ { 'name', 'unlimited', 'literal', add_VM('handleerror') },
+ { 'operator', 'unlimited', 'executable', operators.get, 'get' },
+ { 'operator', 'unlimited', 'executable', operators.exec, 'exec' },
+ }
+ push_execstack { 'array', 'unlimited', 'executable', add_VM(proc), 1, #proc, 'i' }
+ else
+ return
+ end
+ end
+ end
+ end
+ end
+
+ local function close()
+ for i=1,#initializers do
+ initializers[i](true)
+ end
+ enabled = false
+ end
+
+ local function open(options)
+ enabled = true
+ local starttime = os.clock()
+ local stoptime = nil
+ for i=1,#initializers do
+ initializers[i]()
+ end
+ if type(options) == "table" then
+ devicename = options.device or "pdf"
+ findfont = options.findfont or findfont
+ randomseed = options.randomseed or randomseed -- todo
+ calculatebox = options.calculatebox
+ else
+ devicename = "pdf"
+ end
+ device = devices[devicename] or devices.pdf
+ operators.initgraphics()
+ for i=1,#errornames do
+ interpret(formatters["errordict /%s %s put"](errornames[i],generic_error_proc), INITDEBUG)
+ end
+ -- set up the error handler
+ interpret("systemdict /= { 20 string cvs print } bind put", INITDEBUG)
+ interpret("systemdict /prompt { (PS>) print flush } bind put", INITDEBUG)
+ interpret(format("errordict /handleerror %s bind put", generic_handleerror_proc), INITDEBUG)
+ interpret("systemdict /handleerror {errordict /handleerror get exec } bind put", INITDEBUG)
+ -- user dict initializations
+ interpret(format("/quit { stop } bind def"), INITDEBUG)
+ interpret(format("userdict /#copies 1 put"), INITDEBUG)
+ local job = {
+ runtime = 0,
+ interpret = interpret,
+ boundingbox = boundingbox,
+ close = function()
+ close()
+ local runtime = os.clock() - starttime
+ job.runtime = runtime
+ return runtime
+ end,
+ }
+ return job
+ end
+
+ escrito.open = open
+
+ if context then
+
+ function escrito.convert(options)
+ if type(options) == "table" then
+ local data = options.data
+ if not data or data == "" then
+ local buffer = options.buffer
+ local filename = options.filename -- needs escaping
+ if buffer and buffer ~= "" then
+ data = buffers.getcontent(buffer)
+ elseif filename and filename ~= "" then
+ data = io.loaddata(filename) -- use resolver
+ end
+ end
+ if data and data ~= "" then
+ local e = open(options)
+-- print(data)
+ e.interpret(data)
+ return e.close()
+ end
+ end
+ return 0
+ end
+
+ end
+
+ escrito.devices = devices
+
+end
+
+return escrito
diff --git a/tex/context/modules/mkiv/m-escrito.mkiv b/tex/context/modules/mkiv/m-escrito.mkiv
new file mode 100644
index 000000000..763857918
--- /dev/null
+++ b/tex/context/modules/mkiv/m-escrito.mkiv
@@ -0,0 +1,184 @@
+%D \module
+%D [ file=m-escrito,
+%D version=2015.09.27,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=ESCRITO,
+%D author={Taco Hoekwater \& Hans Hagen},
+%D date=\currentdate,
+%D copyright={\CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{m-escrito}{1.001}
+
+%D This is a fun project and not meant for production (yet). It's a follow up on a
+%D project by Taco presented at a Bacho\TeX\ meeting years ago. I probably messed up
+%D the code so much that some things don't work but then, fonts are not really
+%D supported well anyway. However for simple \POSTSCRIPT\ things work out ok.
+%D
+%D I (Hans) will occasionally have a look at the code. Who knows what our trips to
+%D \TeX\ meetings lead to.
+
+\unprotect
+
+\unexpanded\def\startESCRITOgraphic#1#2#3#4%
+ {\dontleavehmode
+ \begingroup
+ \MPllx#1\onebasepoint
+ \MPlly#2\onebasepoint
+ \MPurx#3\onebasepoint
+ \MPury#4\onebasepoint
+ \setbox\b_meta_graphic\hbox\bgroup}
+
+\unexpanded\def\stopESCRITOgraphic
+ {\egroup
+ \setbox\b_meta_graphic\ruledhbox\bgroup
+ \kern-\MPllx\raise-\MPlly\box\b_meta_graphic
+ \egroup
+ \wd\b_meta_graphic\dimexpr\MPurx-\MPllx\relax
+ \ht\b_meta_graphic\dimexpr\MPury-\MPlly\relax
+ \dp\b_meta_graphic\zeropoint
+ \box\b_meta_graphic
+ \endgroup}
+
+\unexpanded\def\flushESCRITOtext#1#2#3% no fratures so pretty weak, better use overlays
+ {\smash{\rlap{\definedfont[#1 at #2bp]#3}}}
+
+\unexpanded\def\stopESCRITO
+ {\edef\p_option{\namedbufferparameter{ESCRITO}\c!option}%
+ \ctxlua{escrito.convert {
+ buffer = "\thedefinedbuffer{ESCRITO}",
+ calculatebox = \ifx\p_option\v!fit true\else false\fi,
+ }}}
+
+\unexpanded\def\processESCRITO[#1]%
+ {\begingroup
+ \getdummyparameters[\c!file=,\c!option=,#1]%
+ \edef\p_option{\dummyparameter\c!option}%
+ \ctxlua{escrito.convert {
+ filename = "\dummyparameter\c!file",
+ calculatebox = \ifx\p_option\v!fit true\else false\fi,
+ }}%
+ \endgroup}
+
+\definebuffer
+ [ESCRITO]
+
+\setupbuffer
+ [ESCRITO]
+ [\c!option=,
+ \c!after=\processESCRITO]
+
+\protect
+
+% This will move to m-escrito.lua once we know how to deal with it ... no time
+% now.
+
+\startluacode
+
+ local literal = nodes.pool.register(node.new("whatsit",nodes.whatsitcodes.pdfliteral))
+ literal.mode = 0
+
+ local function newliteral(result)
+ local l = nodes.copy(literal)
+ l.data = result
+ return l
+ end
+
+ local p = escrito.devices.pdf
+
+ function p.startpage(llx,lly,urx,ury)
+ context.startESCRITOgraphic(llx,lly,urx,ury)
+ end
+
+ function p.stoppage()
+ context.stopESCRITOgraphic()
+ end
+
+ function p.flushpage(result)
+ context(newliteral(result))
+ end
+
+ -- todo
+
+ local fontnames = { }
+ local fontfiles = { }
+
+ fontnames['NimbusSanL-Regu'] = 'Sans'
+ fontnames['StandardSymL'] = 'rpsyr'
+ fontnames['dejavuserif-regular'] = 'dejavuserif-regular'
+
+ function p.showfont(object)
+ local color = object.color
+ local ctype = object.colortype
+ local matrix = object.matrix
+ local text = object.string
+ local size = object.fontmatrix[1] * 1000
+ local result = { "q" }
+ context(newliteral(formatters['%f %f %f %f %f %f cm'](matrix[1],matrix[2],matrix[3],matrix[4],matrix[5],matrix[6])))
+ if ctype == "rgb" then
+ local r, g, b = color[1], color[2], color[3]
+ context(newliteral(formatters["%f %f %f rg %f %f %f RG"](r,g,b,r,g,b)))
+ elseif ctype == "cmyk" then
+ local c, m, y, k = color[1], color[2], color[3], color[4]
+ context(newliteral(formatters["%f %f %f k %f %f %f K"](c,m,y,k,c,m,y,k)))
+ elseif ctype == "gray" then
+ context(newliteral(formatters["%f g %f G"](color,color)))
+ end
+ context.flushESCRITOtext(fontnames[object.fontname],size,text)
+ context(newliteral("Q"))
+ end
+
+ local function findfont(fontname)
+ return fontfiles[fontname]
+ end
+
+\stopluacode
+
+\continueifinputfile{m-escrito.mkiv}
+
+\starttext
+
+% \startluacode
+% local n = 5
+% for i=1,n do
+% context.startTEXpage()
+% local runtime = escrito.convert { filename = "tiger.eps", calculatebox = true }
+% context.par()
+% context("calculated boundingbox, time: %s",runtime)
+% context.stopTEXpage()
+% end
+% for i=1,n do
+% context.startTEXpage()
+% local runtime = escrito.convert { filename = "tiger.eps", calculatebox = false }
+% context.par()
+% context("built in boundingbox, time: %s",runtime)
+% context.stopTEXpage()
+% end
+% \stopluacode
+
+\startTEXpage
+ \startESCRITO
+ (tiger.eps) run
+ \stopESCRITO
+\stopTEXpage
+
+\startTEXpage
+ \setupbuffer[ESCRITO][option=fit]%
+ \startESCRITO
+ (tiger.eps) run
+ \stopESCRITO
+\stopTEXpage
+
+\startTEXpage
+ \processESCRITO[file=tiger.eps]
+\stopTEXpage
+
+\startTEXpage
+ \processESCRITO[file=tiger.eps,option=fit]
+\stopTEXpage
+
+\stoptext
+
diff --git a/tex/context/modules/mkiv/m-fields.mkiv b/tex/context/modules/mkiv/m-fields.mkiv
new file mode 100644
index 000000000..46b77f8d3
--- /dev/null
+++ b/tex/context/modules/mkiv/m-fields.mkiv
@@ -0,0 +1,70 @@
+%D \module
+%D [ file=m-fields,
+%D version=2010.03.14,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=Fields,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+%D A rather old example of field usage is the following. It
+%D makes no sense to have this in the core.
+%D
+%D \starttyping
+%D before \fillinfield[oeps]{whatever} after
+%D \stoptyping
+
+\startJSpreamble{FillInField} used later
+ function CheckFillInField(right) {
+ if (event.value.toLowerCase() == right.toLowerCase()) {
+ event.target.hidden = true ;
+ }
+ event.value = ""
+ }
+\stopJSpreamble
+
+\newcount\noffillinfields
+
+\definefieldcategory
+ [fillinfield]
+ [\c!n=1024,
+ \c!height=\strutht,
+ \c!depth=\strutdp,
+ \c!align=\v!middle,
+ \c!color=red,
+ \c!fieldframecolor=blue,
+ \c!fieldbackgroundcolor=\s!white,
+ \c!validate=JS(CheckFillInField{\therightanswer})]
+
+\def\fillinfield
+ {\dosingleempty\dofillinfield}
+
+\def\dofillinfield[#1]#2%
+ {\dontleavehmode
+ \hbox
+ {\forgetall
+ \global\advance\noffillinfields\plusone
+ \edef\currentfillinfieldname{fillinfield:\number\noffillinfields}%
+ \useJSscripts[ans]%
+ \definefieldbody
+ [\currentfillinfieldname]
+ [\c!type=\v!line,
+ \c!category=fillinfield]%
+ \doifelsenothing{#1}
+ {\def\therightanswer{#2}}
+ {\def\therightanswer{#1}}%
+ \setbox0\hbox{\strut#2}%
+ \setbox2\hbox{\strut\therightanswer}%
+ \dimen0=\dimexpr\ifdim\wd0>\wd2 \wd0 \else \wd2 \fi + .2em\relax
+ \hbox to \wd0
+ {\wd0\zeropoint
+ \box0
+ \hss\fieldbody[\currentfillinfieldname][\c!width=\dimen0]\hss}}}
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/m-format.mkiv b/tex/context/modules/mkiv/m-format.mkiv
new file mode 100644
index 000000000..7cedd803f
--- /dev/null
+++ b/tex/context/modules/mkiv/m-format.mkiv
@@ -0,0 +1,411 @@
+%D \module
+%D [ file=m-formay,
+%D version=ancient,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Ancient Formatting Code,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Here is some code that I had laying around and had forgotten
+%D about. Let's make it a module and see if there is interest in
+%D such things.
+
+% \defineformatblock [poem]
+% \defineformatsegment [verse] % [poem]
+% \defineformatline [line] % [verse]
+%
+% \startpoem [title] [author]
+% \startverse [ref]
+% \startline [ref]
+%
+% block : voor na tussen *tekstletter *tekstkleur
+%
+% segment : voor na tussen *tekstletter *tekstkleur
+% : links rechts linkeroffset rechteroffset
+% : ?marge *evenmarge *onevenmarge breedte
+% : nummer *nummercommando *conversie
+% : nummerletter nummerkleur *label
+%
+% line : voor na tussen *tekstletter *tekstkleur
+% : nummer *nummercommando *conversie
+% : nummerletter nummerkleur *label
+%
+% * = todo
+
+\unprotect
+
+\definesystemvariable {fx} % format block
+\definesystemvariable {fy} % format segment
+\definesystemvariable {fz} % format line
+
+\def\defineformatblock
+ {\dodoubleempty\dodefineformatblock}
+
+\def\dodefineformatblock[#1][#2]%
+ {\setupformatblock
+ [#1]
+ [\c!before=\blank,\c!after=\blank,\c!inbetween=\blank,
+ \c!textstyle=,\c!textcolor=,#2]%
+ \setvalue{\e!start#1}{\startformatblock[#1]}%
+ \setvalue{\e!stop #1}{\stopformatblock}}
+
+\def\setupformatblock
+ {\dodoubleempty\dosetupformatblock}
+
+\def\setupformatblock[#1]%
+ {\getparameters[\??fx#1]}
+
+\def\startformatblock[#1]%
+ {\dotriplegroupempty\dostartformatblock{#1}}
+
+\def\dostartformatblock#1#2#3
+ {\bgroup
+ \getvalue{\??fx#1\c!before}
+ \doglobal\newcounter\formatsegmentcounter
+ \doglobal\newcounter\formatlinecounter
+ \doglobal\newcounter\formatlinesubcounter
+ \doglobal\newcounter\formatlinemaxcounter
+ \doifsomething{#2}{\leftaligned{#2}\getvalue{\??fx#1\c!inbetween}}
+ \def\stopformatblock%
+ {\doifsomething{#3}{\getvalue{\??fx#1\c!inbetween}\leftaligned{#3}}
+ \getvalue{\??fx#1\c!after}
+ \egroup}}
+
+\def\defineformatsegment
+ {\dodoubleempty\dodefineformatsegment}
+
+\def\dodefineformatsegment[#1][#2]%
+ {\setupformatsegment
+ [#1]
+ [\c!before=\blank,\c!after=\blank,\c!inbetween=\blank,
+ \c!textstyle=,\c!textcolor=,\c!left=,\c!right=,
+ \c!leftoffset=\!!zeropoint,\c!rightoffset=\!!zeropoint,
+ %\c!margin=\!!zeropoint,\c!evenmargin=\!!zeropoint,\c!oddmargin=\hsize,
+ \c!width=\hsize,\c!numberstyle=,\c!numbercolor=,\c!number=\v!no,
+ \c!numbercommand=,\c!conversion=,\c!label=,
+ #2]%
+ \setvalue{\e!start#1}{\startformatsegment[#1]}%
+ \setvalue{\e!stop #1}{\stopformatsegment}}
+
+\def\setupformatsegment
+ {\dodoubleempty\dosetupformatsegment}
+
+\def\setupformatsegment[#1]%
+ {\getparameters[\??fy#1]}
+
+\def\placeformatsegmentcounter
+ {\formatsegmentcounter\quad\hphantom{\placeformatlinecounter}}
+
+\def\placeformatlinecounter
+ {\formatlinecounter}
+
+\def\startformatsegment[#1]%
+ {\bgroup
+ \doifelsevalue{\??fy#1\c!number}\v!yes
+ {\def\doplaceformatsegmentcounter
+ {\inleftmargin
+ {\doattributes{\??fy#1}\c!numberstyle\c!numbercolor
+ {\placeformatsegmentcounter}}}}
+ {\let\doplaceformatsegmentcounter\relax}%
+ \getvalue{\??fy#1\c!before}
+ \doglobal\increment\formatsegmentcounter
+ \def\formatrightoffset{\getvalue{\??fy#1\c!rightoffset}}
+ \def\formatleftoffset {\getvalue{\??fy#1\c!leftoffset}}
+ \def\formatminwidth {\getvalue{\??fy#1\c!minwidth}}
+ \def\formatwidth {\getvalue{\??fy#1\c!width}}
+ %\def\formatmargin {\getvalue{\??fy#1\c!margin}}
+ \def\formatbefore {\getvalue{\??fy#1\c!before}}
+ \def\formatinbetween {\getvalue{\??fy#1\c!inbetween}}
+ \def\formatafter {\getvalue{\??fy#1\c!after}}
+ \def\formatleft {\getvalue{\??fy#1\c!left}}
+ \def\formatright {\getvalue{\??fy#1\c!right}}
+ \@@segmentvarianta
+ \getvalue{@@segmentvariant\getvalue{\??fy#1\c!alternative}}
+ \def\stopformatsegment
+ {\getvalue{\??fy#1\c!after}
+ \egroup}}
+
+\newif\iftraceformatblock
+
+\def\@@segmentvarianta % ragged right, symbols
+ {\let\formatraggedness\raggedright
+ \let\dostartformatline\dostartformatlineab
+ \let\formatleftfirst\relax \let\formatrightfirst\hfill
+ \let\formatleftnext \hfill \let\formatrightnext \relax}
+
+\def\@@segmentvariantb % ragged right, equal parts, symbols
+ {\let\formatraggedness\raggedcenter
+ \let\dostartformatline\dostartformatlineab
+ \let\formatleftfirst\relax \let\formatrightfirst\hfill
+ \let\formatleftnext \hfill \let\formatrightnext \relax}
+
+\def\@@segmentvariantc % ragged right
+ {\let\formatraggedness\veryraggedright
+ \let\dostartformatline\dostartformatlinecde
+ \let\formatleftnext\relax \let\formatrightnext\hfill}
+
+\def\@@segmentvariantd % ragged center
+ {\let\formatraggedness\veryraggedcenter
+ \let\dostartformatline\dostartformatlinecde
+ \let\formatleftnext\hfill \let\formatrightnext\hfill}
+
+\def\@@segmentvariante % ragged left
+ {\let\formatraggedness\veryraggedleft
+ \let\dostartformatline\dostartformatlinecde
+ \let\formatleftnext\hfill \let\formatrightnext\relax}
+
+\def\defineformatline
+ {\dodoubleempty\dodefineformatline}
+
+\def\dodefineformatline[#1][#2]%
+ {\setupformatline
+ [#1]
+ [\c!before=\blank,\c!after=\blank,\c!inbetween=\blank,
+ \c!textstyle=,\c!textcolor=,
+ \c!number=\v!no,\c!numbercommand=,\c!conversion=,
+ \c!numberstyle=,\c!numbercolor=,\c!label=,
+ #2]%
+ \setvalue{\e!start#1}{\startformatline[#1]}%
+ \setvalue{\e!stop #1}{\stopformatline}}
+
+\def\setupformatline
+ {\dodoubleempty\dosetupformatline}
+
+\def\setupformatline[#1]%
+ {\getparameters[\??fz#1]}
+
+\newconditional\formatforcedbreak
+
+\def\startformatline[#1]%
+ {\bgroup
+ \doifelsevalue{\??fz#1\c!number}\v!yes
+ {\def\doplaceformatlinecounter
+ {\inleftmargin
+ {\doattributes{\??fz#1}\c!numberstyle\c!numbercolor
+ {\placeformatlinecounter}}}}
+ {\let\doplaceformatlinecounter\relax}%
+ \global\setfalse\formatforcedbreak
+ \def\\{\break\global\settrue\formatforcedbreak}%
+ \hsize\formatwidth
+ \doglobal\increment\formatlinecounter
+ \par
+ \nobreak
+ \def\stopformatline
+ {\unskip\unskip\unskip\unskip\unskip\egroup
+ \let\doplaceformatsegmentcounter\relax}
+ \postponenotes
+ \dowithnextbox{\dostartformatline}\hbox\bgroup\ignorespaces}
+
+\def\dostartformatlineab
+ {%\dosetleftskipadaption\formatmargin
+ %\advance\hsize-\leftskipadaption\relax
+ \ifdim\nextboxwd>\hsize
+ \beginofshapebox
+ \forgetall
+ \hangafter\plusone
+ \hangindent\formatleftoffset
+ \formatraggedness
+ \hskip\formatrightoffset
+ \unhbox\nextbox\par
+ \endofshapebox
+ %\advance\hsize \leftskipadaption
+ \doglobal\newcounter\formatlinesubcounter
+ \reshapebox
+ {\doglobal\increment\formatlinesubcounter}
+ \global\let\formatlinemaxcounter\formatlinesubcounter
+ \reshapebox
+ {\doglobal\decrement\formatlinesubcounter
+ \ifnum\formatlinesubcounter=\zerocount
+ \doplaceformatsegmentcounter
+ \doplaceformatlinecounter
+ \hskip-\formatrightoffset
+ %\hskip\leftskipadaption
+ \formatleftfirst
+ \unhbox\shapebox
+ \ifnum\formatlinemaxcounter>\plusone
+ \ifx\formatright\empty\else
+ \shapedhbox to \zeropoint{\formatright\hss}%
+ \fi
+ \fi
+ \formatrightfirst
+ \iftraceformatblock
+ \ruledhskip\formatrightoffset\hskip-\formatrightoffset
+ \fi
+ \else
+ %\hskip\leftskipadaption
+ \iftraceformatblock
+ \ruledhskip\formatleftoffset\hskip-\formatleftoffset
+ \fi
+ \formatleftnext
+ \ifx\formatleft\empty\else
+ \shapedhbox to \zeropoint{\hss\formatleft}%
+ \fi
+ \unhbox\shapebox
+ \formatrightnext
+ \fi}
+ \flushshapebox
+ \else
+ \dontleavehmode\hbox
+ {\doplaceformatsegmentcounter
+ \doplaceformatlinecounter
+ %\hskip\leftskipadaption
+ \formatleftfirst
+ \unhbox\nextbox
+ \formatrightfirst}
+ \fi
+ \par
+ \egroup}
+
+\def\dostartformatlinecde
+ {%\dosetleftskipadaption\formatmargin
+ %\advance\hsize -\leftskipadaption\relax
+ \dimen0=\hsize
+ \ifconditional\formatforcedbreak\else
+ \ifdim\formatminwidth>\zeropoint\relax
+ \ifdim\nextboxwd>\hsize
+ \doloop
+ {\global\dimen1=\dimen0
+ \beginofshapebox
+ \hsize\dimen0
+ \forgetall
+ \formatraggedness
+ \unhcopy\nextbox\par
+ \endofshapebox
+ \reshapebox
+ {\setbox\scratchbox=\hbox{\unhbox\shapebox}%
+ \ifdim\wd\scratchbox<\dimen1
+ \global\dimen1=\wd\scratchbox
+ \fi}
+ \ifdim\dimen1<\formatminwidth\relax
+ \advance\dimen0 by -.25em
+ \else
+ \exitloop
+ \fi
+ \ifdim\dimen0<10em
+ \dimen0=\hsize
+ \exitloop
+ \fi}
+ \fi
+ \fi
+ \fi
+ \beginofshapebox
+ \hsize\dimen0
+ \forgetall
+ \formatraggedness
+ \unhcopy\nextbox\par
+ \endofshapebox
+ %\advance\hsize \leftskipadaption
+ \doglobal\newcounter\formatlinesubcounter
+ \reshapebox
+ {\doglobal\increment\formatlinesubcounter}%
+ \global\let\formatlinemaxcounter\formatlinesubcounter
+ \reshapebox
+ {\doglobal\decrement\formatlinesubcounter
+ \ifnum\formatlinesubcounter=\zerocount
+ \doplaceformatsegmentcounter
+ \doplaceformatlinecounter
+ \fi
+ %\hskip\leftskipadaption
+ \formatleftnext
+ \unhbox\shapebox
+ \formatrightnext\strut}% strut prevents unskip
+ \flushshapebox
+ \par
+ \egroup}
+
+\defineformatblock[poem]
+ [\c!before=\blank,
+ \c!inbetween={\blank[\v!medium]},
+ \c!after=\blank]
+
+\defineformatsegment[verse]
+ [\c!alternative=\v!left,
+ \c!width=\hsize,
+ %\c!margin=\!!zeropoint,
+ \c!before={\blank[\v!medium]},
+ \c!after={\blank[\v!medium]},
+ \c!inbetween={\blank[\v!medium]},
+ \c!leftoffset=3em,
+ \c!rightoffset=2em,
+ \c!minwidth=5em,
+ \c!left={$[$\enspace},
+ \c!right={\enspace$]$}]
+
+\defineformatline[line]
+ []
+
+\protect
+
+\doifnotmode{demo} {\endinput}
+
+% evt defineblank[formatbefore,formatinbetween,formatafter]
+
+%\showframe \traceformatblocktrue
+
+\usemodule[visual]
+
+\setuplayout[height=middle,topspace=1cm,header=0pt,footer=0pt]
+\setupbodyfont[10pt]
+
+% All interfaces supported, but testing with english; todo:
+% more options, more alternatives, inheritance and mixed
+% definitions, and so.
+
+\starttext
+
+\startbuffer
+\startbuffer[poem]
+\startpoem{A Random Poem}{Hans Hagen}
+ \startverse
+ \startline \fakewords{4}{8} \stopline
+ \startline \fakewords{4}{8} \stopline
+ \startline \fakewords{4}{8} \stopline
+ \startline \fakewords{4}{8} \stopline
+ \stopverse
+ \startverse
+ \startline \fakewords{4}{8} \stopline
+ \startline \fakewords{4}{8} \stopline
+ \startline \fakewords{4}{8} \stopline
+ \startline \fakewords{4}{8} \stopline
+ \stopverse
+\stoppoem
+\stopbuffer
+
+\setupformatsegment[verse][width=.4\hsize,number=yes,numberstyle=slanted]
+\setupformatline [line] [number=yes,numberstyle=smallslanted]
+
+\startbuffer[x]
+\setupformatsegment[verse][leftoffset=0pt,rightoffset=0pt,left=,right=]
+\stopbuffer
+
+\section{Alternative A}
+
+\setupformatsegment[verse][alternative=a] {\getbuffer[poem]}
+\setupformatsegment[verse][alternative=a] {\getbuffer[x,poem]}
+
+\section{Alternative B}
+
+\setupformatsegment[verse][alternative=b] {\getbuffer[poem]}
+\setupformatsegment[verse][alternative=b] {\getbuffer[x,poem]}
+
+\section{Alternative C}
+
+\setupformatsegment[verse][alternative=c] {\getbuffer[poem]}
+
+\section{Alternative D}
+
+\setupformatsegment[verse][alternative=d] {\getbuffer[poem]}
+
+\section{Alternative E}
+
+\setupformatsegment[verse][alternative=e] {\getbuffer[poem]}
+\stopbuffer
+
+\typebuffer \getbuffer
+
+\stoptext
diff --git a/tex/context/modules/mkiv/m-graph.mkiv b/tex/context/modules/mkiv/m-graph.mkiv
new file mode 100644
index 000000000..62c4ec4cb
--- /dev/null
+++ b/tex/context/modules/mkiv/m-graph.mkiv
@@ -0,0 +1,133 @@
+%D \module
+%D [ file=m-graph,
+%D version=2008.09.08,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=\METAPOST\ graph module support,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% We just assume lua specification instead of the graph ones that
+% are limited by what mp can do. We support @ as replacement for
+% the percent symbol. We also add a specifier when no one is given.
+
+\unprotect
+
+\defineMPinstance
+ [graph]
+ [\s!format=metafun,
+ \s!extensions=\v!yes,
+ \s!initializations=\v!yes,
+ \c!method=\s!double]
+
+\startMPdefinitions{graph}
+ if unknown context_grap : input mp-grap.mpiv ; fi ;
+\stopMPdefinitions
+
+\protect
+
+\continueifinputfile{m-graph.mkiv}
+
+%D We put this test here as in \type {meta-tex.mkiv} it would abort due to redefinition
+%D of namespaces.
+
+\starttext
+
+\startMPpage[instance=graph]
+ label.rt(format("@g","1e-8"), (0, 0)) ;
+ label.rt(format("@g","1e+8"), (2cm, 0)) ;
+ label.rt(format("@g","1e-10"), (0, -0.5cm)) ;
+ label.rt(format("@g","1e+10"), (2cm,-0.5cm)) ;
+ label.rt(format("@g","1e-12"), (0, -1.0cm)) ;
+ label.rt(format("@g","1e+12"), (2cm,-1.0cm)) ;
+ label.rt(format("@g","1e-0"), (0, -1.5cm)) ;
+ label.rt(format("@g","1e+0"), (2cm,-1.5cm)) ;
+ label.rt(format("@g","1"), (0, -2.0cm)) ;
+ label.rt(format("@g","1"), (2cm,-2.0cm)) ;
+ label.rt(format("@g","1e-102"),(0, -2.5cm)) ;
+ label.rt(format("@g","1e+102"),(2cm,-2.5cm)) ;
+ currentpicture := currentpicture shifted (-4cm,0) ;
+ %
+ label.rt(format("@j","1e-8"), (0, 0)) ;
+ label.rt(format("@j","1e+8"), (2cm, 0)) ;
+ label.rt(format("@j","1e-10"), (0, -0.5cm)) ;
+ label.rt(format("@j","1e+10"), (2cm,-0.5cm)) ;
+ label.rt(format("@j","1e-12"), (0, -1.0cm)) ;
+ label.rt(format("@j","1e+12"), (2cm,-1.0cm)) ;
+ label.rt(format("@j","1e-0"), (0, -1.5cm)) ;
+ label.rt(format("@j","1e+0"), (2cm,-1.5cm)) ;
+ label.rt(format("@j","1"), (0, -2.0cm)) ;
+ label.rt(format("@j","1"), (2cm,-2.0cm)) ;
+ label.rt(format("@j","1e-102"),(0, -2.5cm)) ;
+ label.rt(format("@j","1e+102"),(2cm,-2.5cm)) ;
+ label.rt(formatted("(@f,@f)",(1.23,4.56)),(0cm,-3.0cm)) ;
+ label.rt(formatted("(@i,@i)",(1.23,4.56)),(0cm,-3.5cm)) ;
+ label.rt(formatted("(@g,@g)",(1.23,4.56)),(0cm,-4.0cm)) ;
+ label.rt(formatted("(@e,@e)",(1.23,4.56)),(0cm,-4.5cm)) ;
+ label.rt(formatted("(@j,@j)",(1.23,4.56)),(0cm,-5.0cm)) ;
+\stopMPpage
+
+\stoptext
+
+% \startMPpage[instance=graph]
+% draw begingraph(3in,2in);
+% gdraw "t:/metapost/grphdata/agepop91.d";
+% endgraph;
+% \stopMPpage
+
+% \startMPpage[instance=graph]
+% draw begingraph(3in,2in);
+% gdraw "agepop91.d" plot btex $\bullet$ etex;
+% endgraph;
+% \stopMPpage
+
+% \startMPpage[instance=graph]
+% draw begingraph(3in,2in);
+% glabel.lft(btex \vbox{\hbox{Population} \hbox{in millions}} etex, OUT);
+% glabel.bot(btex Age in years etex, OUT);
+% gdraw "agepopm.d";
+% endgraph;
+% \stopMPpage
+
+% \startMPpage[instance=graph]
+% draw begingraph(3in,2in);
+% glabel.lft(btex \vbox{\hbox{Population} \hbox{in millions}} etex, OUT);
+% glabel.bot(btex Age in years etex, OUT);
+% setrange(origin, whatever,whatever);
+% gdraw "agepopm.d";
+% endgraph;
+% \stopMPpage
+
+% \startMPpage[instance=graph]
+% draw begingraph(2.3in,2in);
+% setcoords(log,log);
+% glabel.lft(btex Seconds etex,OUT);
+% glabel.bot(btex Matrix size etex,
+% OUT);
+% gdraw "matmul.d" dashed evenly;
+% glabel.ulft(btex Standard etex,8);
+% gdraw "matmul.d";
+% glabel.lrt(btex Strassen etex,7);
+% endgraph;
+% \stopMPpage
+
+% \startMPpage[instance=graph]
+% draw begingraph(6.5cm,4.5cm);
+% setrange(80,0, 90,whatever);
+% glabel.bot(btex Year etex, OUT);
+% glabel.lft(btex \vbox{\hbox{Emissions in} \hbox{thousands of}
+% \hbox{metric tons} \hbox{(heavy line)}}etex, OUT);
+% gdraw "lead.d" withpen pencircle scaled 1.5pt;
+% autogrid(,otick.lft);
+% setcoords(linear,linear);
+% setrange(80,0, 90,whatever);
+% glabel.rt(btex \vbox{\hbox{Micrograms} \hbox{per cubic}
+% \hbox{meter of air} \hbox{(thin line)}}etex, OUT);
+% gdraw "lead.d";
+% autogrid(otick.bot,otick.rt);
+% endgraph;
+% \stopMPpage
diff --git a/tex/context/modules/mkiv/m-hemistich.mkiv b/tex/context/modules/mkiv/m-hemistich.mkiv
new file mode 100644
index 000000000..7a849d415
--- /dev/null
+++ b/tex/context/modules/mkiv/m-hemistich.mkiv
@@ -0,0 +1,120 @@
+%D \module
+%D [ file=m-hemistich,
+%D version=2013.08.26,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=Hemistiches,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is an experimental module for Idris. More is possible but not now.
+
+\unprotect
+
+\installcorenamespace{hemistich}
+
+\installcommandhandler \??hemistich {hemistich} \??hemistich
+
+\setuphemistich
+ [\c!width=\v!local,
+ \c!distance=4\emwidth,
+ \c!separator=\vl\hskip.25em\vl]
+
+\unexpanded\def\hemistiches
+ {\dosingleempty\dohemistiches}
+
+\unexpanded\def\dohemistiches
+ {\dodohemistiches\empty}
+
+\unexpanded\def\dodohemistiches#1[#2]#3#4%
+ {\dontleavehmode
+ \begingroup
+ \doifelseassignment{#2}
+ {\edef\currenthemistich{#1}%
+ \setupcurrenthemistich[#2]}
+ {\def\currenthemistich{#2}}%
+ \doifelse{\hemistichparameter\c!width}\v!local
+ {\scratchwidth\availablehsize}
+ {\scratchwidth\hemistichparameter\c!width\relax}%
+ \spaceskip\zeropoint\s!plus\plusone\s!fill\relax
+ \dostarttagged\t!division\currenthemistich
+ \hbox to \scratchwidth\bgroup
+ \scratchwidth.5\dimexpr\scratchwidth-\hemistichparameter\c!distance\relax
+ \hbox to \scratchwidth\bgroup
+ \dostarttagged\t!construct\c!lefttext
+ \usehemistichstyleandcolor\c!leftstyle\c!leftcolor#3%
+ \dostoptagged
+ \egroup
+ \hss
+ \begingroup
+ \dostarttagged\t!construct\c!separator
+ \usehemistichstyleandcolor\c!separatorstyle\c!separatorcolor
+ \hemistichparameter\c!separator
+ \dostoptagged
+ \endgroup
+ \hss
+ \hbox to \scratchwidth\bgroup
+ \dostarttagged\t!construct\c!righttext
+ \usehemistichstyleandcolor\c!rightstyle\c!rightcolor#4%
+ \dostoptagged
+ \egroup
+ \egroup
+ \dostoptagged
+ \endgroup}
+
+\unexpanded\def\hemistichescaesura#1#2#3%
+ {\dodohemistiches\empty[\c!separator={#2}]{#1}{#3}}
+
+\appendtoks
+ \setvalue{\currenthemistich}{\dohemistiches{\currenthemistich}}%
+\to \everydefinehemistich
+
+\protect
+
+\continueifinputfile{m-hemistich.mkiv}
+
+\setuphemistich
+ [leftcolor=darkred,
+ separatorcolor=darkgreen,
+ rightcolor=darkblue]
+
+\setupwhitespace
+ [big]
+
+\starttext
+
+% \righttoleft
+
+\hemistichescaesura{left side of the brain}{equals}{right side of the brain}
+
+\hemistiches{left side of the brain}{right side of the brain}
+
+\startitemize
+ \startitem
+ \hemistiches{left side of the brain}{right side of the brain}
+ \startitemize
+ \startitem
+ \hemistiches{left side of the brain}{right side of the brain}
+ \startitemize
+ \startitem
+ \hemistiches{left side of the brain}{right side of the brain}
+ \stopitem
+ \stopitemize
+ \stopitem
+ \stopitemize
+ \startitem
+ \hemistiches{left side of the brain}{right side of the brain}
+ \stopitem
+ \stopitem
+\stopitemize
+
+\startitemize
+\item \hemistiches{left side of the brain}{right side of the brain}
+\stopitemize
+
+\stoptext
+
diff --git a/tex/context/modules/mkiv/m-ipsum.mkiv b/tex/context/modules/mkiv/m-ipsum.mkiv
new file mode 100644
index 000000000..1c5901d86
--- /dev/null
+++ b/tex/context/modules/mkiv/m-ipsum.mkiv
@@ -0,0 +1,198 @@
+%D \module
+%D [ file=m-ipsum,
+%D version=2012.07.19,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=Ipsum,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D After some discussing on the mailing list I made this example of
+%D an implementation. Of course there can be alternatives as it's a
+%D nice exercise in module writing.
+
+\startluacode
+
+local patterns = lpeg.patterns
+
+local variables = interfaces.variables
+local v_random = variables.random
+
+local lowercase = characters.lower
+
+local ipsum = { }
+moduledata.ipsum = ipsum
+
+local data = { }
+
+local function getfiledata(settings)
+ local filename = settings.filename or ""
+ local filedata = data[filename]
+ if not filedata then
+ local text = resolvers.loadtexfile(filename) or ""
+ local paragraphs = lpeg.match(patterns.paragraphs,text) or { }
+ local sentences = lpeg.match(patterns.sentences, text) or { }
+ local words = lpeg.match(patterns.words, text) or { }
+ for i=1,#words do
+ words[i] = lowercase(words[i])
+ end
+ filedata = {
+ -- [variables.paragraphs] = paragraphs,
+ [variables.paragraph] = paragraphs,
+ [variables.lines] = sentences,
+ [variables.line] = sentences,
+ [variables.words] = words,
+ [variables.word] = words,
+ }
+ -- inspect(filedata)
+ data[filename] = filedata
+ end
+ local d = filedata[settings.alternative or v_paragraph] or filedata[v_paragraph] or { }
+ local nd = #d
+ local n = settings.n
+ if n ~= v_random then
+ n = tonumber(n) or 0
+ if n == 0 then
+ n = nd
+ end
+ end
+ return d, n, nd
+end
+
+function moduledata.ipsum.typeset(settings)
+ local d, n, nd = getfiledata(settings)
+ if nd > 0 then
+ context(settings.before)
+ if n == v_random then
+ context(settings.left)
+ context(d[math.random(1,nd)])
+ context(settings.right)
+ else
+ for i=1,n do
+ context(settings.left)
+ context(d[i])
+ context(settings.right)
+ if i < n then
+ context(settings.inbetween)
+ end
+ end
+ end
+ context(settings.after)
+ end
+end
+
+function moduledata.ipsum.direct(settings)
+ local d, n, nd = getfiledata(settings)
+ if nd == 0 then
+ -- nothing
+ elseif n == v_random then
+ context(d[math.random(1,nd)])
+ else
+ for i=1,n do
+ context(d[i])
+ if i < n then
+ context(settings.separator)
+ end
+ end
+ end
+end
+
+\stopluacode
+
+\unprotect
+
+\installnamespace {ipsum}
+
+\installcommandhandler \????ipsum {ipsum} \????ipsum
+
+\setupipsum
+ [\c!file=lorem,
+ \c!alternative=\v!paragraph,
+ %\c!language=,
+ %\c!styl=,
+ %\c!color=,
+ \c!n=0,
+ \c!left=,
+ \c!right=,
+ \c!before=,
+ \c!after=,
+ \c!separator=,
+ \c!inbetween=]
+
+\installactionhandler{ipsum} % grouped
+
+\startsetups[handler:action:ipsum]
+ \useipsumstyleandcolor\c!style\c!color
+ \uselanguageparameter\ipsumparameter
+ \ctxlua{moduledata.ipsum.typeset {
+ alternative = "\ipsumparameter\c!alternative",
+ filename = "\ipsumparameter\c!file",
+ n = "\ipsumparameter\c!n",
+ left = "\luaescapestring{\ipsumparameter\c!left}",
+ right = "\luaescapestring{\ipsumparameter\c!right}",
+ before = "\luaescapestring{\ipsumparameter\c!before}",
+ after = "\luaescapestring{\ipsumparameter\c!after}",
+ inbetween = "\luaescapestring{\ipsumparameter\c!inbetween}",
+ }}
+\stopsetups
+
+\def\directipsum#1% only one argument, expanded
+ {\ctxlua{moduledata.ipsum.typeset {
+ alternative = "\namedipsumparameter{#1}\c!alternative",
+ filename = "\namedipsumparameter{#1}\c!file",
+ n = "\namedipsumparameter{#1}\c!n",
+ separator = "\luaescapestring{\ipsumparameter\c!separator}",
+ }}
+}
+
+\protect
+
+\continueifinputfile{m-ipsum.mkiv}
+
+\setupbodyfont[dejavu,11pt]
+
+\starttext
+
+ \ipsum[alternative=paragraph,before=\blank,after=\blank,language=la]
+
+ \ipsum[alternative=lines,n=2,right=\par,before=\blank,after=\blank,language=la]
+
+ \ipsum[alternative=lines,n=random,before=\blank,after=\blank,language=la]
+
+ \ipsum[alternative=lines,before=\startitemize,after=\stopitemize,left=\startitem,right=\stopitem,language=la]
+
+ \ipsum[alternative=words,left=(,right=),inbetween=\space,language=la]
+
+ \page
+
+ \defineipsum
+ [ward]
+ [file=ward,
+ before=\blank,
+ after=\blank]
+
+ \defineipsum
+ [ward:itemize]
+ [ward]
+ [alternative=lines,
+ before={\startitemize[packed]},
+ after=\stopitemize,
+ left=\startitem,
+ right=\stopitem]
+
+ \defineipsum
+ [ward:title]
+ [ward]
+ [alternative=lines,
+ n=random]
+
+ \subject{\directipsum{ward:title}}
+
+ \ipsum[ward]
+ \ipsum[ward:itemize]
+
+\stoptext
diff --git a/tex/context/modules/mkiv/m-json.mkiv b/tex/context/modules/mkiv/m-json.mkiv
new file mode 100644
index 000000000..329aa0f31
--- /dev/null
+++ b/tex/context/modules/mkiv/m-json.mkiv
@@ -0,0 +1,30 @@
+%D \module
+%D [ file=m-json,
+%D version=2012.08.03,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Json,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This module is a side effect of looking into json. Currently there are
+%D only a few helpers:
+%D
+%D \starttyping
+%D moduledata.json.tolua (str)
+%D moduledata.json.tostring(val)
+%D \stoptyping
+%D
+%D Nothing spectacular but maybe handy to have around.
+
+\startmodule [json]
+
+% check for: utilities.json
+
+\registerctxluafile{util-jsn}{}
+
+\stopmodule
diff --git a/tex/context/modules/mkiv/m-layout.mkiv b/tex/context/modules/mkiv/m-layout.mkiv
new file mode 100644
index 000000000..5ccf0e987
--- /dev/null
+++ b/tex/context/modules/mkiv/m-layout.mkiv
@@ -0,0 +1,102 @@
+%D \module
+%D [ file=m-layout,
+%D version=2004.01.16,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Additional Layouts,
+%D author={Hans Hagen \& Ton Otten},
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% layout-preset - thf th tf
+
+%D This is a preliminary module, don't depend on these dimensions yet.
+
+\readsysfile{lang-frq}\donothing\donothing
+\readsysfile{lang-frd}\donothing\donothing
+
+\unprotect
+
+% \def\layouthwratio{\withoutpt\the\dimexpr8\paperheight/\dimexpr(\paperwidth/ 8192)\relax}
+% \def\layouthwratio{\withoutpt\the\dimexpr4\paperheight/\dimexpr(\paperwidth/16384)\relax}
+% \def\layouthwratio{\withoutpt\the\dimexpr2\paperheight/\dimexpr(\paperwidth/32768)\relax}
+
+\def\layouthwratio
+ {\withoutpt\the\dimexpr2\paperheight/(\paperwidth/32768)\relax}
+
+\def\layouthfheight
+ {\dimexpr\layoutparameter\c!header+\layoutparameter\c!headerdistance+
+ \layoutparameter\c!footer+\layoutparameter\c!footerdistance\relax}
+
+\startsetups[preset-1]
+
+ \xdef\layoutwidth {\dimexpr\layoutparameter\c!width\relax}
+ \gdef\layoutheight{\dimexpr\layouthwratio\dimexpr\layoutwidth\relax+\layouthfheight\relax}
+
+\stopsetups
+
+\definelayout
+ [preset-1-1]
+ [\c!preset=preset-1,
+ \c!backspace=\dimexpr(\paperwidth-\layoutwidth)/2\relax,
+ \c!width=\dimexpr2\paperwidth/3\relax,
+ \c!cutspace=\dimexpr(\paperwidth-\layoutwidth)/2\relax,
+ \c!margin=\dimexpr(\paperwidth-\layoutwidth)/3\relax,
+ \c!header=2\lineheight,
+ \c!headerdistance=\lineheight,
+ \c!height=\v!middle, % \layoutheight
+ \c!footerdistance=\layoutparameter\c!headerdistance, % \lineheight,
+ \c!footer=\layoutparameter\c!header, % 2\lineheight,
+ \c!topspace=\dimexpr1\dimexpr\paperheight-(\layoutheight+\layouthfheight)\relax/3\relax,
+ \c!bottomspace=\dimexpr2\dimexpr\paperheight-(\layoutheight+\layouthfheight)\relax/3\relax]
+
+\startsetups[preset-2]
+
+ \gdef\layouthstep{\dimexpr\paperwidth /\layoutparameter\c!columns\relax}
+ \gdef\layoutvstep{\dimexpr\paperheight/\layoutparameter\c!columns\relax}
+
+\stopsetups
+
+\definelayout
+ [preset-2-1]
+ [\c!preset=preset-2,
+ \c!columns=12,
+ \c!backspace=\layouthstep,
+ \c!width=\v!middle,
+ \c!cutspace=2\layouthstep,
+ \c!margin=\layouthstep,
+ \c!header=2\lineheight,
+ \c!headerdistance=\lineheight,
+ \c!height=\v!middle, % \layoutheight
+ \c!footerdistance=\layoutparameter\c!headerdistance,
+ \c!footer=\layoutparameter\c!header,
+ \c!topspace=\dimexpr\layoutvstep-\layoutparameter\c!header-\layoutparameter\c!headerdistance\relax,
+ \c!bottomspace=\dimexpr(2\layoutvstep)-\layoutparameter\c!header-\layoutparameter\c!headerdistance\relax]
+
+\definelayout
+ [preset-2-2]
+ [\c!preset=preset-2,
+ \c!columns=12,
+ \c!backspace=\layouthstep,
+ \c!width=\v!middle,
+ \c!cutspace=2\layouthstep,
+ \c!margin=\layouthstep,
+ \c!header=2\lineheight,
+ \c!headerdistance=\lineheight,
+ \c!height=\v!middle, % \layoutheight
+ \c!footerdistance=\layoutparameter\c!headerdistance,
+ \c!footer=\layoutparameter\c!header,
+ \c!topspace=\layoutvstep,
+ \c!bottomspace=\layoutvstep] % maybe 1.5
+
+% \setuplayout[preset-1-1] test \showframe \page
+% \setuplayout[preset-1-1][width=65\averagecharwidth] \setuplayout[preset-1-1] test \showframe \page
+% \setuplayout[preset-2-1] test \showframe \page
+% \setuplayout[preset-2-1][columns=10] \setuplayout[preset-2-1] test \showframe \page
+% \setuplayout[preset-2-2] test \showframe \page
+% \setuplayout[preset-2-2][columns=10] \setuplayout[preset-2-2] test \showframe \page
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/m-logcategories.mkiv b/tex/context/modules/mkiv/m-logcategories.mkiv
new file mode 100644
index 000000000..954cd58c4
--- /dev/null
+++ b/tex/context/modules/mkiv/m-logcategories.mkiv
@@ -0,0 +1,3 @@
+\starttext
+ \showlogcategories
+\stoptext
diff --git a/tex/context/modules/mkiv/m-markdown.lua b/tex/context/modules/mkiv/m-markdown.lua
new file mode 100644
index 000000000..1f9402f60
--- /dev/null
+++ b/tex/context/modules/mkiv/m-markdown.lua
@@ -0,0 +1,824 @@
+if not modules then modules = { } end modules ['m-markdown'] = {
+ version = 1.002,
+ comment = "companion to m-markdown.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "see below",
+ license = "see context related readme files"
+}
+
+--[[
+Copyright (C) 2009 John MacFarlane / Khaled Hosny / Hans Hagen
+
+The main parser is derived from the lunamark parser written by John MacFarlane. You
+can download lunamark from:
+
+ http://github.com/jgm/lunamark.git
+
+Khaled Hosny provided the context writer for lunamark and that was used as starting
+point for the mapping. The original code can be fetched from the above location.
+
+While playing with the original code I got the feeling that lpeg could perform better.
+The slowdown was due to the fact that the parser's lpeg was reconstructed each time a
+nested parse was needed. After changing that code a bit I could bring down parsing of
+some test code from 2 seconds to less than 0.1 second so I decided to stick to this
+parser instead of writing my own. After all, the peg code looks pretty impressive and
+visiting Johns pandoc pages is worth the effort:
+
+ http://johnmacfarlane.net/pandoc/
+
+The code here is mostly meant for processing snippets embedded in a context
+documents and is no replacement for pandoc at all. Therefore an alternative is to use
+pandoc in combination with Aditya's filter module.
+
+As I changed (and optimized) the original code, it will be clear that all errors
+are mine. Eventually I might also adapt the parser code a bit more. When I ran into of
+closure stack limitations I decided to flatten the code. The following implementation
+seems to be a couple of hundred times faster than what I started with which is not that
+bad.
+
+This is a second rewrite. The mentioned speed gain largely depended on the kind of
+content: blocks, references and items can be rather demanding. Also, There were
+some limitations with respect to the captures. So, table storage has been removed in
+favor of strings, and nesting has been simplified. The first example at the end of this
+file now takes .33 seconds for 567KB code (resulting in over 1MB) so we're getting there.
+
+There will be a third rewrite eventually.
+]]--
+
+-- todo: we have better quote and tag scanners in ctx
+-- todo: provide an xhtml mapping
+-- todo: add a couple of extensions
+-- todo: check patches to the real peg
+
+local type, next, tonumber = type, next, tonumber
+local lower, upper, gsub, rep, gmatch, format, length = string.lower, string.upper, string.gsub, string.rep, string.gmatch, string.format, string.len
+local concat = table.concat
+local P, R, S, V, C, Ct, Cg, Cb, Cmt, Cc, Cf, Cs = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Cg, lpeg.Cb, lpeg.Cmt, lpeg.Cc, lpeg.Cf, lpeg.Cs
+local lpegmatch = lpeg.match
+local utfbyte, utfchar = utf.byte, utf.char
+
+moduledata = moduledata or { }
+moduledata.markdown = moduledata.markdown or { }
+local markdown = moduledata.markdown
+
+local nofruns, nofbytes, nofhtmlblobs = 0, 0, 0
+
+---------------------------------------------------------------------------------------------
+
+local nestedparser
+local syntax
+
+nestedparser = function(str) return lpegmatch(syntax,str) end
+
+---------------------------------------------------------------------------------------------
+
+local asterisk = P("*")
+local dash = P("-")
+local plus = P("+")
+local underscore = P("_")
+local period = P(".")
+local hash = P("#")
+local ampersand = P("&")
+local backtick = P("`")
+local less = P("<")
+local more = P(">")
+local space = P(" ")
+local squote = P("'")
+local dquote = P('"')
+local lparent = P("(")
+local rparent = P(")")
+local lbracket = P("[")
+local rbracket = P("]")
+local slash = P("/")
+local equal = P("=")
+local colon = P(":")
+local semicolon = P(";")
+local exclamation = P("!")
+
+local digit = R("09")
+local hexdigit = R("09","af","AF")
+local alphanumeric = R("AZ","az","09")
+
+local doubleasterisks = P("**")
+local doubleunderscores = P("__")
+local fourspaces = P(" ")
+
+local any = P(1)
+local always = P("")
+
+local tab = P("\t")
+local spacechar = S("\t ")
+local spacing = S(" \n\r\t")
+local newline = P("\r")^-1 * P("\n")
+local spaceornewline = spacechar + newline
+local nonspacechar = any - spaceornewline
+local optionalspace = spacechar^0
+local spaces = spacechar^1
+local eof = - any
+local nonindentspace = space^-3
+local blankline = optionalspace * C(newline)
+local blanklines = blankline^0
+local skipblanklines = (optionalspace * newline)^0
+local linechar = P(1 - newline)
+local indent = fourspaces + (nonindentspace * tab) / ""
+local indentedline = indent /"" * C(linechar^1 * (newline + eof))
+local optionallyindentedline = indent^-1 /"" * C(linechar^1 * (newline + eof))
+local spnl = optionalspace * (newline * optionalspace)^-1
+local specialchar = S("*_`*&[]<!\\")
+local normalchar = any - (specialchar + spaceornewline)
+local line = C((any - newline)^0 * newline)
+ + C(any^1 * eof)
+local nonemptyline = (any - newline)^1 * newline
+
+---------------------------------------------------------------------------------------------
+
+local function lineof(c)
+ return (nonindentspace * (P(c) * optionalspace)^3 * newline * blankline^1)
+end
+
+local lineof_asterisks = lineof(asterisk)
+local lineof_dashes = lineof(dash)
+local lineof_underscores = lineof(underscore)
+
+local bullet = nonindentspace * (plus + (asterisk - lineof_asterisks) + (dash - lineof_dashes)) * spaces
+local enumerator = nonindentspace * digit^1 * period * spaces
+
+---------------------------------------------------------------------------------------------
+
+local openticks = Cg(backtick^1, "ticks")
+local closeticks = space^-1 * Cmt(C(backtick^1) * Cb("ticks"), function(s,i,a,b) return #a == #b and i end)
+local intickschar = (any - S(" \n\r`"))
+ + (newline * -blankline)
+ + (space - closeticks)
+ + (backtick^1 - closeticks)
+local inticks = openticks * space^-1 * C(intickschar^1) * closeticks
+
+---------------------------------------------------------------------------------------------
+
+local leader = space^-3
+local nestedbrackets = P { lbracket * ((1 - lbracket - rbracket) + V(1))^0 * rbracket }
+local tag = lbracket * C((nestedbrackets + 1 - rbracket)^0) * rbracket
+local url = less * C((1-more)^0) * more
+ + C((1-spacing- rparent)^1) -- sneaky: ) for resolver
+local title_s = squote * lpeg.C((1-squote )^0) * squote
+local title_d = dquote * lpeg.C((1-dquote )^0) * dquote
+local title_p = lparent * lpeg.C((1-rparent)^0) * rparent
+local title = title_s + title_d + title_p
+local optionaltitle = ((spacing^0 * title * spacechar^0) + lpeg.Cc(""))
+
+local references = { }
+
+local function register_link(tag,url,title)
+ tag = lower(gsub(tag, "[ \n\r\t]+", " "))
+ references[tag] = { url, title }
+end
+
+local function direct_link(label,url,title) -- title is typical html thing
+ return label, url, title
+end
+
+local function indirect_link(label,tag)
+ if tag == "" then
+ tag = label
+ end
+ tag = lower(gsub(tag, "[ \n\r\t]+", " "))
+ local r = references[tag]
+ if r then
+ return label, r[1], r[2]
+ else
+ return label, tag, ""
+ end
+end
+
+local define_reference_parser = (leader * tag * colon * spacechar^0 * url * optionaltitle) / register_link
+local direct_link_parser = tag * spacechar^0 * lparent * (url + Cc("")) * optionaltitle * rparent / direct_link
+local indirect_link_parser = tag * spacechar^0 * tag / indirect_link
+
+local rparser = (define_reference_parser+1)^0
+
+local function referenceparser(str)
+ references = { }
+ lpegmatch(rparser,str)
+end
+
+-- local reftest = [[
+-- [1]: <http://example.com/>
+-- [3]:http://example.com/ (Optional Title Here)
+-- [2]: http://example.com/ 'Optional Title Here'
+-- [a]: http://example.com/ "Optional *oeps* Title Here"
+-- ]]
+--
+-- local linktest = [[
+-- [This link] (http://example.net/)
+-- [an example] (http://example.com/ "Title")
+-- [an example][1]
+-- [an example] [2]
+-- ]]
+--
+-- lpeg.match((define_reference_parser+1)^0,reftest)
+--
+-- inspect(references)
+--
+-- lpeg.match((direct_link_parser/print + indirect_link_parser/print + 1)^0,linktest)
+
+---------------------------------------------------------------------------------------------
+
+local blocktags = table.tohash {
+ "address", "blockquote" , "center", "dir", "div", "p", "pre",
+ "li", "ol", "ul", "dl", "dd",
+ "form", "fieldset", "isindex", "menu", "noframes", "frameset",
+ "h1", "h2", "h3", "h4", "h5", "h6",
+ "hr", "ht", "script", "noscript",
+ "table", "tbody", "tfoot", "thead", "th", "td", "tr",
+}
+
+----- htmlattributevalue = squote * C((any - (blankline + squote))^0) * squote
+----- + dquote * C((any - (blankline + dquote))^0) * dquote
+----- + (any - S("\t >"))^1 -- any - tab - space - more
+----- htmlattribute = (alphanumeric + S("_-"))^1 * spnl * (equal * spnl * htmlattributevalue)^-1 * spnl
+----- htmlcomment = P("<!--") * (any - P("-->"))^0 * P("-->")
+
+----- htmltag = less * spnl * slash^-1 * alphanumeric^1 * spnl * htmlattribute^0 * slash^-1 * spnl * more
+-----
+----- blocktag = Cmt(C(alphanumeric^1), function(s,i,a) return blocktags[lower(a)] and i, a end)
+-----
+----- openblocktag = less * Cg(blocktag, "opentag") * spnl * htmlattribute^0 * more
+----- closeblocktag = less * slash * Cmt(C(alphanumeric^1) * Cb("opentag"), function(s,i,a,b) return lower(a) == lower(b) and i end) * spnl * more
+----- selfclosingblocktag = less * blocktag * spnl * htmlattribute^0 * slash * more
+-----
+----- displayhtml = Cs { "HtmlBlock",
+----- InBlockTags = openblocktag * (V("HtmlBlock") + (any - closeblocktag))^0 * closeblocktag,
+----- HtmlBlock = C(V("InBlockTags") + selfclosingblocktag + htmlcomment),
+----- }
+-----
+----- inlinehtml = Cs(htmlcomment + htmltag)
+
+-- There is no reason to support crappy html, so we expect proper attributes.
+
+local htmlattributevalue = squote * C((any - (blankline + squote))^0) * squote
+ + dquote * C((any - (blankline + dquote))^0) * dquote
+local htmlattribute = (alphanumeric + S("_-"))^1 * spnl * equal * spnl * htmlattributevalue * spnl
+
+local htmlcomment = P("<!--") * (any - P("-->"))^0 * P("-->")
+local htmlinstruction = P("<?") * (any - P("?>" ))^0 * P("?>" )
+
+-- We don't care too much about matching elements and there is no reason why display elements could not
+-- have inline elements so the above should be patched then. Well, markdown mixed with html is not meant
+-- for anything else than webpages anyway.
+
+local blocktag = Cmt(C(alphanumeric^1), function(s,i,a) return blocktags[lower(a)] and i, a end)
+
+local openelement = less * alphanumeric^1 * spnl * htmlattribute^0 * more
+local closeelement = less * slash * alphanumeric^1 * spnl * more
+local emptyelement = less * alphanumeric^1 * spnl * htmlattribute^0 * slash * more
+
+local displaytext = (any - less)^1
+local inlinetext = displaytext / nestedparser
+
+local displayhtml = #(less * blocktag * spnl * htmlattribute^0 * more)
+ * Cs { "HtmlBlock",
+ InBlockTags = openelement * (V("HtmlBlock") + displaytext)^0 * closeelement,
+ HtmlBlock = (V("InBlockTags") + emptyelement + htmlcomment + htmlinstruction),
+ }
+
+local inlinehtml = Cs { "HtmlBlock",
+ InBlockTags = openelement * (V("HtmlBlock") + inlinetext)^0 * closeelement,
+ HtmlBlock = (V("InBlockTags") + emptyelement + htmlcomment + htmlinstruction),
+ }
+
+---------------------------------------------------------------------------------------------
+
+local hexentity = ampersand * hash * S("Xx") * C(hexdigit ^1) * semicolon
+local decentity = ampersand * hash * C(digit ^1) * semicolon
+local tagentity = ampersand * C(alphanumeric^1) * semicolon
+
+---------------------------------------------------------------------------------------------
+
+-- --[[
+
+local escaped = {
+ ["{" ] = "",
+ ["}" ] = "",
+ ["$" ] = "",
+ ["&" ] = "",
+ ["#" ] = "",
+ ["~" ] = "",
+ ["|" ] = "",
+ ["%%"] = "",
+ ["\\"] = "",
+}
+
+for k, v in next, escaped do
+ escaped[k] = "\\char" .. utfbyte(k) .. "{}"
+end
+
+local function c_string(s) -- has to be done more often
+ return (gsub(s,".",escaped))
+end
+
+local c_linebreak = "\\crlf\n" -- is this ok?
+local c_space = " "
+
+local function c_paragraph(c)
+ return c .. "\n\n" -- { "\\startparagraph ", c, " \\stopparagraph\n" }
+end
+
+local function listitem(c)
+ return format("\n\\startitem\n%s\n\\stopitem\n",nestedparser(c))
+end
+
+local function c_tightbulletlist(c)
+ return format("\n\\startmarkdownitemize[packed]\n%s\\stopmarkdownitemize\n",c)
+end
+
+local function c_loosebulletlist(c)
+ return format("\n\\startmarkdownitemize\n\\stopmarkdownitemize\n",c)
+end
+
+local function c_tightorderedlist(c)
+ return format("\n\\startmarkdownitemize[n,packed]\n%s\\stopmarkdownitemize\n",c)
+end
+
+local function c_looseorderedlist(c)
+ return format("\n\\startmarkdownitemize[n]\n%s\\stopmarkdownitemize\n",c)
+end
+
+local function c_inline_html(content)
+ nofhtmlblobs = nofhtmlblobs + 1
+ return format("\\markdowninlinehtml{%s}",content)
+end
+
+local function c_display_html(content)
+ nofhtmlblobs = nofhtmlblobs + 1
+ return format("\\startmarkdowndisplayhtml\n%s\n\\stopmarkdowndisplayhtml",content)
+end
+
+local function c_emphasis(c)
+ return format("\\markdownemphasis{%s}",c)
+end
+
+local function c_strong(c)
+ return format("\\markdownstrong{%s}",c)
+end
+
+local function c_blockquote(c)
+ return format("\\startmarkdownblockquote\n%s\\stopmarkdownblockquote\n",nestedparser(c))
+end
+
+local function c_verbatim(c)
+ return format("\\startmarkdowntyping\n%s\\stopmarkdowntyping\n",c)
+end
+
+local function c_code(c)
+ return format("\\markdowntype{%s}",c)
+end
+
+local levels = { "", "", "", "", "", "" }
+
+local function c_start_document()
+ levels = { "", "", "", "", "", "" }
+ return ""
+end
+
+local function c_stop_document()
+ return concat(levels,"\n") or ""
+end
+
+local function c_heading(level,c)
+ if level > #levels then
+ level = #levels
+ end
+ local finish = concat(levels,"\n",level) or ""
+ for i=level+1,#levels do
+ levels[i] = ""
+ end
+ levels[level] = "\\stopstructurelevel"
+ return format("%s\\startstructurelevel[markdown][title={%s}]\n",finish,c)
+end
+
+local function c_hrule()
+ return "\\markdownrule\n"
+end
+
+local function c_link(lab,src,tit)
+ return format("\\goto{%s}[url(%s)]",nestedparser(lab),src)
+end
+
+local function c_image(lab,src,tit)
+ return format("\\externalfigure[%s]",src)
+end
+
+local function c_email_link(address)
+ return format("\\goto{%s}[url(mailto:%s)]",c_string(address),address)
+end
+
+local function c_url_link(url)
+ return format("\\goto{%s}[url(%s)]",c_string(url),url)
+end
+
+local function f_heading(c,n)
+ return c_heading(n,c)
+end
+
+local function c_hex_entity(s)
+ return utfchar(tonumber(s,16))
+end
+
+local function c_dec_entity(s)
+ return utfchar(tonumber(s))
+end
+
+local function c_tag_entity(s)
+ return s -- we can use the default resolver
+end
+
+--]]
+
+---------------------------------------------------------------------------------------------
+
+--[[
+
+local escaped = {
+ ["<"] = "&lt;",
+ [">"] = "&gt;",
+ ["&"] = "&amp;",
+ ['"'] = "&quot;",
+}
+
+local function c_string(s) -- has to be done more often
+ return (gsub(s,".",escaped))
+end
+
+local c_linebreak = "<br/>"
+local c_space = " "
+
+local function c_paragraph(c)
+ return format("<p>%s</p>\n", c)
+end
+
+local function listitem(c)
+ return format("<li>%s</li>",nestedparser(c))
+end
+
+local function c_tightbulletlist(c)
+ return format("<ul>\n%s\n</ul>\n",c)
+end
+
+local function c_loosebulletlist(c)
+ return format("<ul>\n%s\n</ul>\n",c)
+end
+
+local function c_tightorderedlist(c)
+ return format("<ol>\n%s\n</ol>\n",c)
+end
+
+local function c_looseorderedlist(c)
+ return format("<ol>\n%s\n</ol>\n",c)
+end
+
+local function c_inline_html(content)
+ nofhtmlblobs = nofhtmlblobs + 1
+ return content
+end
+
+local function c_display_html(content)
+ nofhtmlblobs = nofhtmlblobs + 1
+ return format("\n%s\n",content)
+end
+
+local function c_emphasis(c)
+ return format("<em>%s</em>",c)
+end
+
+local function c_strong(c)
+ return format("<strong>%s</strong>",c)
+end
+
+local function c_blockquote(c)
+ return format("<blockquote>\n%s\n</blockquote>",nestedparser(c))
+end
+
+local function c_verbatim(c)
+ return format("<pre><code>%s</code></pre>",c)
+end
+
+local function c_code(c)
+ return format("<code>%s</code>",c)
+end
+
+local c_start_document = ""
+local c_stop_document = ""
+
+local function c_heading(level,c)
+ return format("<h%d>%s</h%d>\n",level,c,level)
+end
+
+local function c_hrule()
+ return "<hr/>\n"
+end
+
+local function c_link(lab,src,tit)
+ local titattr = #tit > 0 and format(" title=%q",tit) or ""
+ return format("<a href=%q%s>%s</a>",src,titattr,nestedparser(lab))
+end
+
+local function c_image(lab,src,tit)
+ return format("<img href=%q title=%q>%s</a>",src,tit,nestedparser(lab))
+end
+
+local function c_email_link(address)
+ return format("<a href=%q>%s</a>","mailto:",address,c_escape(address))
+end
+
+local function c_url_link(url)
+ return format("<a href=%q>%s</a>",url,c_string(url))
+end
+
+local function f_heading(c,n)
+ return c_heading(n,c)
+end
+
+local function c_hex_entity(s)
+ return utfchar(tonumber(s,16))
+end
+
+local function c_dec_entity(s)
+ return utfchar(tonumber(s))
+end
+
+local function c_tag_entity(s)
+ return format("&%s;",s)
+end
+
+--]]
+
+---------------------------------------------------------------------------------------------
+
+local Str = normalchar^1 / c_string
+local Space = spacechar^1 / c_space
+local Symbol = specialchar / c_string
+local Code = inticks / c_code
+
+local HeadingStart = C(hash * hash^-5) / length
+local HeadingStop = optionalspace * hash^0 * optionalspace * newline * blanklines
+local HeadingLevel = equal^3 * Cc(1)
+ + dash ^3 * Cc(2)
+
+local NormalEndline = optionalspace * newline * -(
+ blankline
+ + more
+ + HeadingStart
+ + ( line * (P("===")^3 + P("---")^3) * newline )
+ ) / c_space
+
+local LineBreak = P(" ") * NormalEndline / c_linebreak
+
+local TerminalEndline = optionalspace * newline * eof / ""
+
+local Endline = LineBreak
+ + TerminalEndline
+ + NormalEndline
+
+local AutoLinkUrl = less * C(alphanumeric^1 * P("://") * (any - (newline + more))^1) * more / c_url_link
+local AutoLinkEmail = less * C((alphanumeric + S("-_+"))^1 * P("@") * (any - (newline + more))^1) * more / c_email_link
+
+local DirectLink = direct_link_parser / c_link
+local IndirectLink = indirect_link_parser / c_link
+
+local ImageLink = exclamation * (direct_link_parser + indirect_link_parser) / c_image -- we can combine this with image ... smaller lpeg
+
+local UlOrStarLine = asterisk^4
+ + underscore^4
+ + (spaces * S("*_")^1 * #spaces) / c_string
+
+local EscapedChar = P("\\") * C(P(1 - newline)) / c_string
+
+local InlineHtml = inlinehtml / c_inline_html
+local DisplayHtml = displayhtml / c_display_html
+local HtmlEntity = hexentity / c_hex_entity
+ + decentity / c_dec_entity
+ + tagentity / c_tag_entity
+
+local NestedList = Cs(optionallyindentedline - (bullet + enumerator))^1 / nestedparser
+
+local ListBlockLine = -blankline * -(indent^-1 * (bullet + enumerator)) * optionallyindentedline
+
+local Verbatim = Cs(blanklines * (indentedline - blankline)^1) / c_verbatim
+ * (blankline^1 + eof) -- not really needed, probably capture trailing? we can do that beforehand
+
+local Blockquote = Cs((
+ ((nonindentspace * more * space^-1)/"" * linechar^0 * newline)^1
+ * ((linechar - blankline)^1 * newline)^0
+ * blankline^0
+ )^1) / c_blockquote
+
+local HorizontalRule = (lineof_asterisks + lineof_dashes + lineof_underscores) / c_hrule
+
+local Reference = define_reference_parser / ""
+
+-- could be a mini grammar
+
+local ListBlock = line * ListBlockLine^0
+local ListContinuationBlock = blanklines * indent * ListBlock
+local ListItem = Cs(ListBlock * (NestedList + ListContinuationBlock^0)) / listitem
+
+---- LeadingLines = blankline^0 / ""
+---- TrailingLines = blankline^1 * #(any) / "\n"
+
+syntax = Cs { "Document",
+
+ Document = V("Display")^0,
+
+ Display = blankline -- ^1/"\n"
+ + Blockquote
+ + Verbatim
+ + Reference
+ + HorizontalRule
+ + HeadingStart * optionalspace * Cs((V("Inline") - HeadingStop)^1) * HeadingStop / c_heading
+ + Cs((V("Inline") - Endline)^1) * newline * HeadingLevel * newline * blanklines / f_heading
+ + Cs((bullet /"" * ListItem)^1) * blanklines * -bullet / c_tightbulletlist
+ + Cs((bullet /"" * ListItem * C(blanklines))^1) / c_loosebulletlist
+ + Cs((enumerator /"" * ListItem)^1) * blanklines * -enumerator / c_tightorderedlist
+ + Cs((enumerator /"" * ListItem * C(blanklines))^1) / c_looseorderedlist
+ + DisplayHtml
+ + nonindentspace * Cs(V("Inline")^1)* newline * blankline^1 / c_paragraph
+ + V("Inline")^1,
+
+ Inline = Str
+ + Space
+ + Endline
+ + UlOrStarLine -- still needed ?
+ + doubleasterisks * -spaceornewline * Cs((V("Inline") - doubleasterisks )^1) * doubleasterisks / c_strong
+ + doubleunderscores * -spaceornewline * Cs((V("Inline") - doubleunderscores)^1) * doubleunderscores / c_strong
+ + asterisk * -spaceornewline * Cs((V("Inline") - asterisk )^1) * asterisk / c_emphasis
+ + underscore * -spaceornewline * Cs((V("Inline") - underscore )^1) * underscore / c_emphasis
+ + ImageLink
+ + DirectLink
+ + IndirectLink
+ + AutoLinkUrl
+ + AutoLinkEmail
+ + Code
+ + InlineHtml
+ + HtmlEntity
+ + EscapedChar
+ + Symbol,
+
+}
+
+---------------------------------------------------------------------------------------------
+
+local function convert(str)
+ nofruns = nofruns + 1
+ nofbytes = nofbytes + #str
+ statistics.starttiming(markdown)
+ referenceparser(str)
+ local result = c_start_document() .. nestedparser(str) .. c_stop_document()
+ statistics.stoptiming(markdown)
+ return result
+end
+
+markdown.convert = convert
+
+function markdown.typesetstring(data)
+ if data and data ~= "" then
+ local result = convert(data)
+ context.viafile(result)
+ end
+end
+
+function markdown.typesetbuffer(name)
+ markdown.typesetstring(buffers.getcontent(name))
+end
+
+function markdown.typesetfile(name)
+ local fullname = resolvers.findctxfile(name)
+ if fullname and fullname ~= "" then
+ markdown.typesetstring(io.loaddata(fullname))
+ end
+end
+
+statistics.register("markdown",function()
+ if nofruns > 0 then
+ return format("%s bytes converted, %s runs, %s html blobs, %s seconds used",
+ nofbytes, nofruns, nofhtmlblobs, statistics.elapsedtime(markdown))
+ end
+end)
+
+---------------------------------------------------------------------------------------------
+
+--~ context.starttext()
+--~ moduledata.markdown.convert(str)
+--~ context.stoptext()
+
+if not tex.jobname then
+
+ local one = [[
+Test *123*
+==========
+
+<b>BOLD *BOLD* BOLD</b>
+
+<pre>PRE <b>PRE</b> PRE</pre>
+
+
+* Test
+** Test
+* Test1
+ * Test2
+* Test
+
+Test
+====
+
+> test
+> test **123** *123*
+> test `code`
+
+test
+
+Test
+====
+
+> test
+> test
+> test
+
+test
+oeps
+
+more
+
+ code
+ code
+
+oeps
+
+[an example][a]
+
+[an example] [2]
+
+[a]: http://example.com/ "Optional *oeps* Title Here"
+[2]: http://example.com/ 'Optional Title Here'
+[3]: http://example.com/ (Optional Title Here)
+
+[an example][a]
+
+[an example] [2]
+
+[an [tricky] example](http://example.com/ "Title")
+
+[This **xx** link](http://example.net/)
+ ]]
+
+-- This snippet takes some 4 seconds in the original parser (the one that is
+-- a bit clearer from the perspective of grammars but somewhat messy with
+-- respect to the captures. In the above parser it takes .1 second. Also,
+-- in the later case only memory is the limit.
+
+ local two = [[
+Test
+====
+* Test
+** Test
+* Test
+** Test
+* Test
+
+Test
+====
+
+> test
+> test
+> test
+
+test
+
+Test
+====
+
+> test
+> test
+> test
+
+test
+ ]]
+
+ local function test(str)
+ local n = 1 -- 000
+ local t = os.clock()
+ local one = convert(str)
+ -- print("runtime",1,#str,#one,os.clock()-t)
+ str = string.rep(str,n)
+ local t = os.clock()
+ local two = convert(str)
+ print(two)
+ -- print("runtime",n,#str,#two,os.clock()-t)
+ -- print(format("==============\n%s\n==============",one))
+ end
+
+ -- test(one)
+ -- test(two)
+ -- test(io.read("*all"))
+
+
+end
diff --git a/tex/context/modules/mkiv/m-markdown.mkiv b/tex/context/modules/mkiv/m-markdown.mkiv
new file mode 100644
index 000000000..6e0036513
--- /dev/null
+++ b/tex/context/modules/mkiv/m-markdown.mkiv
@@ -0,0 +1,88 @@
+%D \module
+%D [ file=x-markdown,
+%D version=2011.07.19,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Processing MarkDown,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Modules / MarkDown Renderer}
+
+%D This module deals with markdown which is a document encoding that
+%D some \CONTEXT\ like much. It reminds me of the kind of minimal coding
+%D we used before we ran into \TEX\ and were using a somewhat simple
+%D rendering (pagination, etc) of documents. As I'm no user myself, it
+%D is up to others to provide documentation and examples.
+
+\registerctxluafile{m-markdown}{1.001}
+
+\unprotect
+
+% basic interface
+
+\definebuffer[markdown]
+
+\unexpanded\def\stopmarkdown
+ {\ctxlua{moduledata.markdown.typesetbuffer("\thedefinedbuffer{markdown}")}}
+
+\unexpanded\def\processmarkdownfile#1% maybe [] or both
+ {\ctxlua{moduledata.markdown.typesetfile("#1")}}
+
+\unexpanded\def\markdown#1% maybe [] or both
+ {\ctxlua{moduledata.markdown.typesetstring(\!!bs#1\!!es)}}
+
+% commands
+
+\defineitemgroup
+ [markdownitemize]
+
+\definetyping
+ [markdowntyping]
+
+\definetype
+ [markdowntype]
+
+\definetype
+ [markdowninlinehtml]
+
+\definetyping
+ [markdowndisplayhtml]
+
+\definedelimitedtext
+ [markdownblockquote]
+ [quotation]
+
+\definehighlight
+ [markdownemphasis]
+ [style=\em]
+
+\definehighlight
+ [markdownstrong]
+ [style=\bf]
+
+\definestructurelevels
+ [markdown]
+ [\v!chapter,
+ \v!section,
+ \v!subsection,
+ \v!subsubsection,
+ \v!subsubsubsection,
+ \v!subsubsubsubsection]
+
+\unexpanded\def\markdownrule
+ {\hairline\par}
+
+\protect
+
+\continueifinputfile{m-markdown.mkiv}
+
+\starttext
+ \startmarkdown
+ % some examples needed
+ \stopmarkdown
+\stoptext
diff --git a/tex/context/modules/mkiv/m-mathcrap.mkiv b/tex/context/modules/mkiv/m-mathcrap.mkiv
new file mode 100644
index 000000000..25efd2d5d
--- /dev/null
+++ b/tex/context/modules/mkiv/m-mathcrap.mkiv
@@ -0,0 +1,76 @@
+%D \module
+%D [ file=m-mathcrap,
+%D version=2010.05.30,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Math Crap,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is meant for those who want to use the (incomplete and sort of useless)
+%D unicode superscripts and subscripts. We should look ahead and collapse them
+%D but I will only implement that in calcmath when the need is there. Now the
+%D spacing can be somewhat non optimal but probably that does not matter here.
+%D
+%D \startbuffer
+%D $a₀₁₂₃₄₅₆₇₈₉₋₌₊$
+%D \stopbuffer
+%D
+%D \typebuffer \blank \getbuffer \blank
+
+\unprotect
+
+\unexpanded\def\mathunicodesupercrap#1{\mathortext{{\normalsuperscript{#1}}}{\high{#1}}}
+\unexpanded\def\mathunicodesubcrap #1{\mathortext{{\normalsubscript {#1}}}{\low {#1}}}
+
+\ifdefined\installanddefineactivecharacter\else
+
+ \def\installanddefineactivecharacter #1 #2% we need this as command
+ {\normalexpanded{\noexpand\installactivecharacter \utfchar{#1} }%
+ \defineactivecharacter #1 {#2}}
+
+\fi
+
+\installanddefineactivecharacter "2070 {\mathunicodesupercrap 0}
+\installanddefineactivecharacter "00B9 {\mathunicodesupercrap 1}
+\installanddefineactivecharacter "00B2 {\mathunicodesupercrap 2}
+\installanddefineactivecharacter "00B3 {\mathunicodesupercrap 3}
+\installanddefineactivecharacter "2074 {\mathunicodesupercrap 4}
+\installanddefineactivecharacter "2075 {\mathunicodesupercrap 5}
+\installanddefineactivecharacter "2076 {\mathunicodesupercrap 6}
+\installanddefineactivecharacter "2077 {\mathunicodesupercrap 7}
+\installanddefineactivecharacter "2078 {\mathunicodesupercrap 8}
+\installanddefineactivecharacter "2079 {\mathunicodesupercrap 9}
+\installanddefineactivecharacter "207A {\mathunicodesupercrap +}
+\installanddefineactivecharacter "207B {\mathunicodesupercrap -}
+\installanddefineactivecharacter "207C {\mathunicodesupercrap =}
+\installanddefineactivecharacter "207D {\mathunicodesupercrap (}
+\installanddefineactivecharacter "207E {\mathunicodesupercrap )}
+\installanddefineactivecharacter "207F {\mathunicodesupercrap n}
+
+\installanddefineactivecharacter "2080 {\mathunicodesubcrap 0}
+\installanddefineactivecharacter "2081 {\mathunicodesubcrap 1}
+\installanddefineactivecharacter "2082 {\mathunicodesubcrap 2}
+\installanddefineactivecharacter "2083 {\mathunicodesubcrap 3}
+\installanddefineactivecharacter "2084 {\mathunicodesubcrap 4}
+\installanddefineactivecharacter "2085 {\mathunicodesubcrap 5}
+\installanddefineactivecharacter "2086 {\mathunicodesubcrap 6}
+\installanddefineactivecharacter "2087 {\mathunicodesubcrap 7}
+\installanddefineactivecharacter "2088 {\mathunicodesubcrap 8}
+\installanddefineactivecharacter "2089 {\mathunicodesubcrap 9}
+\installanddefineactivecharacter "208A {\mathunicodesubcrap +}
+\installanddefineactivecharacter "208B {\mathunicodesubcrap -}
+\installanddefineactivecharacter "208C {\mathunicodesubcrap =}
+\installanddefineactivecharacter "208D {\mathunicodesubcrap (}
+\installanddefineactivecharacter "208E {\mathunicodesubcrap )}
+\installanddefineactivecharacter "2090 {\mathunicodesubcrap A}
+\installanddefineactivecharacter "2091 {\mathunicodesubcrap E}
+\installanddefineactivecharacter "2092 {\mathunicodesubcrap O}
+\installanddefineactivecharacter "2093 {\mathunicodesubcrap X}
+%installanddefineactivecharacter "2094 {\mathunicodesubcrap ?} % SCHWAA
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/m-matrix.mkiv b/tex/context/modules/mkiv/m-matrix.mkiv
new file mode 100644
index 000000000..ccb376e39
--- /dev/null
+++ b/tex/context/modules/mkiv/m-matrix.mkiv
@@ -0,0 +1,495 @@
+%D \module
+%D [ file=m-matrix,
+%D version=2014.11.04, % already a year older
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=Matrices,
+%D author={Jeong Dalyoung \& Hans Hagen},
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This code is based on a post by Dalyoung on the context list. After that
+%D we turned it into a module and improved the code a bit. Feel free to ask
+%D us for more. Once we're satisfied, a more general helper l-matrix could
+%D be made. Dalyoung does the clever bits, and Hans only cleanes up and
+%D optimizes a bit.
+
+% \registerctxluafile{l-matrix}{1.001} % not yet
+
+\startmodule[matrix]
+
+\startluacode
+
+local settings_to_hash = utilities.parsers.settings_to_hash
+local formatters = string.formatters
+local copy = table.copy
+local insert = table.insert
+local remove = table.remove
+
+local matrix = { }
+moduledata.matrix = matrix
+
+local f_matrix_slot = formatters["%s_{%s%s}"]
+
+function matrix.symbolic(sym, x, y, nx ,ny) -- symMatrix("a", "m", "n")
+ local nx = nx or 2
+ local ny = ny or nx
+ local function filled(i,y)
+ local mrow = { }
+ for j=1,nx do
+ mrow[#mrow+1] = f_matrix_slot(sym,i,j)
+ end
+ mrow[#mrow+1] = "\\cdots"
+ mrow[#mrow+1] = f_matrix_slot(sym,i,y)
+ return mrow
+ end
+ local function dummy()
+ local mrow = { }
+ for j=1,nx do
+ mrow[#mrow+1] = "\\vdots"
+ end
+ mrow[#mrow+1] = "\\ddots"
+ mrow[#mrow+1] = "\\vdots"
+ return mrow
+ end
+ --
+ local mm = { }
+ for i=1,ny do
+ mm[i] = filled(i,y)
+ end
+ mm[#mm+1] = dummy()
+ mm[#mm+1] = filled(x,y)
+ return mm
+end
+
+-- todo: define a matrix at the tex end so that we have more control
+
+local fences_p = {
+ left = "\\left(\\,",
+ right = "\\,\\right)",
+}
+
+local fences_b = {
+ left = "\\left[\\,",
+ right = "\\,\\right]",
+}
+
+function matrix.typeset(m,options)
+ local options = settings_to_hash(options or "")
+ context.startmatrix(options.determinant and fences_b or fences_p)
+ for i=1, #m do
+ local mi = m[i]
+ for j=1,#mi do
+ context.NC(mi[j])
+ end
+ context.NR()
+ end
+ context.stopmatrix()
+end
+
+-- interchange two rows (i-th, j-th)
+
+function matrix.swap(t,i,j)
+ t[i], t[j] = t[j], t[i]
+end
+
+-- replace i-th row with factor * (i-th row)
+
+function matrix.multiply(m,i,factor)
+ local mi = m[i]
+ for k=1,#mi do
+ mi[k] = factor * mi[k]
+ end
+ return m
+end
+
+-- scalar product "factor * m"
+
+function matrix.scalar(m, factor)
+ for i=1,#m do
+ local mi = m[i]
+ for j=1,#mi do
+ mi[j] = factor * mi[j]
+ end
+ end
+ return m
+end
+
+-- replace i-th row with i-th row + factor * (j-th row)
+
+function matrix.sumrow(m,i,j,factor)
+ local mi = m[i]
+ local mj = m[j]
+ for k=1,#mi do
+ mi[k] = mi[k] + factor * mj[k]
+ end
+end
+
+-- transpose of a matrix
+
+function matrix.transpose(m)
+ local t = { }
+ for j=1,#m[1] do
+ local r = { }
+ for i=1,#m do
+ r[i] = m[i][j]
+ end
+ t[j] = r
+ end
+ return t
+end
+
+-- inner product of two vectors
+
+function matrix.inner(u,v)
+ local nu = #u
+ if nu == 0 then
+ return 0
+ end
+ local nv = #v
+ if nv ~= nu then
+ return 0
+ end
+ local result = 0
+ for i=1,nu do
+ result = result + u[i] * v[i]
+ end
+ return result
+end
+
+-- product of two matrices
+
+function matrix.product(m1,m2)
+ local product = { }
+ if #m1[1] == #m2 then
+ for i=1,#m1 do
+ local m1i = m1[i]
+ local mrow = { }
+ for j=1,#m2[1] do
+ local temp = 0
+ for k=1,#m1[1] do
+ temp = temp + m1i[k] * m2[k][j]
+ end
+ mrow[j] = temp
+ end
+ product[i] = mrow
+ end
+ end
+ return product
+end
+
+local function uppertri(m,sign)
+ local temp = copy(m)
+ for i=1,#temp-1 do
+ local pivot = temp[i][i]
+ if pivot == 0 then
+ local pRow = i +1
+ while temp[pRow][i] == 0 do
+ pRow = pRow + 1
+ if pRow > #temp then -- if there is no nonzero number
+ return temp
+ end
+ end
+ temp[i], temp[pRow] = temp[pRow], temp[i]
+ if sign then
+ sign = -sign
+ end
+ end
+ local mi = temp[i]
+ for k=i+1, #temp do
+ local factor = -temp[k][i]/mi[i]
+ local mk = temp[k]
+ for l=i,#mk do
+ mk[l] = mk[l] + factor * mi[l]
+ end
+ end
+ end
+ if sign then
+ return temp, sign
+ else
+ return temp
+ end
+end
+
+matrix.uppertri = uppertri
+
+function matrix.determinant(m)
+ if #m == #m[1] then
+ local d = 1
+ local t, s = uppertri(m,1)
+ for i=1,#t do
+ d = d * t[i][i]
+ end
+ return s*d
+ else
+ return 0
+ end
+end
+
+local function rowechelon(m,r)
+ local temp = copy(m)
+ local pRow = 1
+ local pCol = 1
+ while pRow <= #temp do
+ local pivot = temp[pRow][pCol]
+ if pivot == 0 then
+ local i = pRow
+ local n = #temp
+ while temp[i][pCol] == 0 do
+ i = i + 1
+ if i > n then
+ -- no nonzero number in a column
+ pCol = pCol + 1
+ if pCol > #temp[pRow] then
+ -- there is no nonzero number in a row
+ return temp
+ end
+ i = pRow
+ end
+ end
+ temp[pRow], temp[i] = temp[i], temp[pRow]
+ end
+ local row = temp[pRow]
+ pivot = row[pCol]
+ for l=pCol,#row do
+ row[l] = row[l]/pivot
+ end
+
+ if r == 1 then
+ -- make the "reduced row echelon form"
+ local row = temp[pRow]
+ for k=1,pRow-1 do
+ local current = temp[k]
+ local factor = -current[pCol]
+ local mk = current
+ for l=pCol,#mk do
+ mk[l] = mk[l] + factor * row[l]
+ end
+ end
+ end
+ -- just make the row echelon form
+ local row = temp[pRow]
+ for k=pRow+1, #temp do
+ local current = temp[k]
+ local factor = -current[pCol]
+ local mk = current
+ for l=pCol,#mk do
+ mk[l] = mk[l] + factor * row[l]
+ end
+ end
+ pRow = pRow + 1
+ pCol = pCol + 1
+
+ if pRow > #temp or pCol > #temp[1] then
+ pRow = #temp + 1
+ end
+ end
+ return temp
+end
+
+matrix.rowechelon = rowechelon
+matrix.rowEchelon = rowechelon
+
+-- solve the linear equation m X = c
+
+local function solve(m,c)
+ local n = #m
+ if n ~= #c then
+ return copy(m)
+ end
+ local newm = copy(m)
+ local temp = copy(c)
+ for i=1,n do
+ insert(newm[i],temp[i])
+ end
+ return rowechelon(newm,1)
+end
+
+matrix.solve = solve
+
+-- find the inverse matrix of m
+
+local function inverse(m)
+ local n = #m
+ local temp = copy(m)
+ if n ~= #m[1] then
+ return temp
+ end
+ for i=1,n do
+ for j=1,n do
+ insert(temp[i],j == i and 1 or 0)
+ end
+ end
+ temp = rowechelon(temp,1)
+ for i=1,n do
+ for j=1,n do
+ remove(temp[i], 1)
+ end
+ end
+ return temp
+end
+
+matrix.inverse = inverse
+
+\stopluacode
+
+\stopmodule
+
+\unexpanded\def\ctxmodulematrix#1{\ctxlua{moduledata.matrix.#1}}
+
+\continueifinputfile{m-matrix.mkiv}
+
+\starttext
+
+\startluacode
+document.DemoMatrixA = {
+ { 0, 2, 4, -4, 1 },
+ { 0, 0, 2, 3, 4 },
+ { 2, 2, -6, 2, 4 },
+ { 2, 0, -6, 9, 7 },
+ { 2, 3, 4, 5, 6 },
+ { 6, 6, -6, 6, 6 },
+}
+
+document.DemoMatrixB = {
+ { 0, 2, 4, -4, 1 },
+ { 0, 0, 2, 3, 4 },
+ { 2, 2, -6, 2, 4 },
+ { 2, 0, -6, 9, 7 },
+ { 2, 2, -6, 2, 4 },
+ { 2, 2, -6, 2, 4 },
+}
+\stopluacode
+
+\startsubject[title={A symbolic matrix}]
+
+\ctxmodulematrix{typeset(moduledata.matrix.symbolic("a", "m", "n"))}
+\ctxmodulematrix{typeset(moduledata.matrix.symbolic("a", "m", "n", 4, 8))}
+
+\stopsubject
+
+\startsubject[title={Swap two rows (2 and 4)}]
+
+\startluacode
+moduledata.matrix.typeset(document.DemoMatrixA)
+context.blank()
+moduledata.matrix.swap(document.DemoMatrixA, 2, 4)
+context.blank()
+moduledata.matrix.typeset(document.DemoMatrixA)
+\stopluacode
+
+\stopsubject
+
+\startsubject[title={Multiply $3 \times r_2$}]
+
+\startluacode
+moduledata.matrix.typeset(document.DemoMatrixA)
+context.blank()
+moduledata.matrix.typeset(moduledata.matrix.multiply(document.DemoMatrixA, 2, 3))
+\stopluacode
+
+\stopsubject
+
+\startsubject[title={Row 2 + $3 \times r_4$}]
+
+\startluacode
+moduledata.matrix.typeset(document.DemoMatrixA)
+context.blank()
+moduledata.matrix.sumrow(document.DemoMatrixA, 2, 3, 4)
+context.blank()
+moduledata.matrix.typeset(document.DemoMatrixA)
+\stopluacode
+
+\stopsubject
+
+\startsubject[title={Transpose a matrix}]
+
+\startluacode
+moduledata.matrix.typeset(document.DemoMatrixA)
+context.blank()
+moduledata.matrix.typeset(moduledata.matrix.transpose(document.DemoMatrixA))
+\stopluacode
+
+\stopsubject
+
+\startsubject[title={The inner product of two vectors}]
+
+\startluacode
+context(moduledata.matrix.inner({ 1, 2, 3 }, { 3, 1, 2 }))
+context.blank()
+context(moduledata.matrix.inner({ 1, 2, 3 }, { 3, 1, 2, 4 }))
+\stopluacode
+
+\startsubject[title={The product of two matrices}]
+
+\startluacode
+moduledata.matrix.typeset(document.DemoMatrixA)
+context.blank()
+moduledata.matrix.typeset(moduledata.matrix.product(document.DemoMatrixA,document.DemoMatrixA))
+\stopluacode
+
+\stopsubject
+
+\startsubject[title={An Upper Triangular Matrix}]
+
+\ctxmodulematrix{typeset(moduledata.matrix.uppertri(document.DemoMatrixB))}
+
+\startsubject[title={A determinant}]
+
+\startluacode
+local m = {
+ { 1, 2, 4 },
+ { 0, 0, 2 },
+ { 2, 2, -6 },
+}
+context(moduledata.matrix.determinant(m))
+\stopluacode
+
+\stopsubject
+
+\startsubject[title={Row echelon form}]
+
+\startluacode
+local m = {
+ { 1, 3, -2, 0, 2, 0, 0 },
+ { 2, 6, -5, -2, 4, -3, -1 },
+ { 0, 0, 5, 10, 0, 15, 5 },
+ { 2, 6, 0, 8, 4, 18, 6 },
+}
+
+moduledata.matrix.typeset(m)
+moduledata.matrix.typeset(moduledata.matrix.rowechelon(m,1))
+\stopluacode
+
+\stopsubject
+
+\startsubject[title={Solving linear equation}]
+
+\startluacode
+local m = {
+ { 1, 3, -2, 0 },
+ { 2, 0, 1, 2 },
+ { 6, -5, -2, 4 },
+ { -3, -1, 5, 10 },
+}
+
+local c = { 5, 2, 6, 8 }
+
+moduledata.matrix.typeset(moduledata.matrix.solve(m,c))
+\stopluacode
+
+\stopsubject
+
+\startsubject[title={Inverse matrix}]
+
+\startcombination[2*1]
+ {\ctxlua{moduledata.matrix.typeset { { 1, 1, 1 }, { 0, 2, 3 }, { 3, 2, 1 } }}} {}
+ {\ctxlua{moduledata.matrix.typeset(moduledata.matrix.inverse { { 1, 1, 1 }, { 0, 2, 3 }, { 3, 2, 1 } })}} {}
+\stopcombination
+
+\stopsubject
+
+\stoptext
diff --git a/tex/context/modules/mkiv/m-mkii.mkiv b/tex/context/modules/mkiv/m-mkii.mkiv
new file mode 100644
index 000000000..dcfd29d20
--- /dev/null
+++ b/tex/context/modules/mkiv/m-mkii.mkiv
@@ -0,0 +1,21 @@
+% todo
+
+\unprotect
+
+\writestatus\m!system{loading some mkii compatibility hacks}
+
+% Compatibility for font-ini
+
+\let\normalxi=\xi
+
+\definebodyfontswitch [xii] [\!!twelvepoint]
+\definebodyfontswitch [xi] [\!!elevenpoint]
+\definebodyfontswitch [x] [\!!tenpoint]
+\definebodyfontswitch [ix] [\!!ninepoint]
+\definebodyfontswitch [viii] [\!!eightpoint]
+\definebodyfontswitch [vii] [\!!sevenpoint]
+\definebodyfontswitch [vi] [\!!sixpoint]
+
+\unexpanded\def\xi{\ifmmode\normalxi\else\elevenpoint\fi}
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/m-mkivhacks.mkiv b/tex/context/modules/mkiv/m-mkivhacks.mkiv
new file mode 100644
index 000000000..3ed002e1d
--- /dev/null
+++ b/tex/context/modules/mkiv/m-mkivhacks.mkiv
@@ -0,0 +1,50 @@
+%D \module
+%D [ file=m-mkivhacks,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Temporary Compatilibility Hacks,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D For Aditya, who needed it for his thesis.
+
+\startluacode
+ -- -- a lot or work
+ --
+ -- mathematics.slots.euler = {
+ -- [0x03B1] = { "mr", 0x0B }, -- alpha
+ -- }
+ --
+ -- mathematics.slots.euler = table.merge(mathematics.slots.traditional,mathematics.slots.euler)
+ --
+ -- versus a quick hack
+
+ document.hacks = document.hacks or { }
+
+ function document.hacks()
+ mathematics.families.lcgreek = mathematics.families.mr
+ mathematics.families.ucgreek = mathematics.families.mr
+ mathematics.families.vargreek = mathematics.families.mr
+
+ mathematics.define(mathematics.slots.euler)
+ end
+\stopluacode
+
+% \usemodule[mkivhacks] \setups{eulermath}
+% \definetypeface[modern][mm][math][euler][default]
+% \usemathcollection[eul]
+% \switchtobodyfont[modern,11pt]
+% \starttext
+% $\alpha$
+% \stoptext
+
+\startsetups eulermath
+ \ctxlua{document.hacks()}
+\stopsetups
+
+\endinput
diff --git a/tex/context/modules/mkiv/m-morse.mkvi b/tex/context/modules/mkiv/m-morse.mkvi
new file mode 100644
index 000000000..a2c20dff7
--- /dev/null
+++ b/tex/context/modules/mkiv/m-morse.mkvi
@@ -0,0 +1,273 @@
+%D \module
+%D [ file=m-morse,
+%D version=2010.12.10,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=Morse,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% todo: act upon the node list
+% make it a buffer operation
+% nice in cld manual
+
+\startluacode
+
+moduledata.morse = moduledata.morse or { }
+local morse = moduledata.morse
+
+local utfcharacters, gsub = string.utfcharacters, string.gsub
+local ucchars, shchars = characters.ucchars, characters.shchars
+
+local codes = {
+
+ ["A"] = "·—",
+ ["B"] = "—···",
+ ["C"] = "—·—·",
+ ["D"] = "—··",
+ ["E"] = "·",
+ ["F"] = "··—·",
+ ["G"] = "——·",
+ ["H"] = "····",
+ ["I"] = "··",
+ ["J"] = "·———",
+ ["K"] = "—·—",
+ ["L"] = "·—··",
+ ["M"] = "——",
+ ["N"] = "—·",
+ ["O"] = "———",
+ ["P"] = "·——·",
+ ["Q"] = "——·—",
+ ["R"] = "·—·",
+ ["S"] = "···",
+ ["T"] = "—",
+ ["U"] = "··—",
+ ["V"] = "···—",
+ ["W"] = "·——",
+ ["X"] = "—··—",
+ ["Y"] = "—·——",
+ ["Z"] = "——··",
+
+ ["0"] = "—————",
+ ["1"] = "·————",
+ ["2"] = "··———",
+ ["3"] = "···——",
+ ["4"] = "····—",
+ ["5"] = "·····",
+ ["6"] = "—····",
+ ["7"] = "——···",
+ ["8"] = "———··",
+ ["9"] = "————·",
+
+ ["."] = "·—·—·—",
+ [","] = "——··——",
+ [":"] = "———···",
+ [";"] = "—·—·—",
+
+ ["?"] = "··——··",
+ ["!"] = "—·—·——",
+
+ ["-"] = "—····—",
+ ["/"] = "—··—· ",
+
+ ["("] = "—·——·",
+ [")"] = "—·——·—",
+
+ ["="] = "—···—",
+ ["@"] = "·——·—·",
+
+ ["'"] = "·————·",
+ ['"'] = "·—··—·",
+
+ ["À"] = "·——·—",
+ ["Å"] = "·——·—",
+ ["Ä"] = "·—·—",
+ ["Æ"] = "·—·—",
+ ["Ç"] = "—·—··",
+ ["É"] = "··—··",
+ ["È"] = "·—··—",
+ ["Ñ"] = "——·——",
+ ["Ö"] = "———·",
+ ["Ø"] = "———·",
+ ["Ü"] = "··——",
+ ["ß"] = "··· ···",
+
+}
+
+morse.codes = codes
+
+local fallbackself = false
+
+local function codefallback(t,k)
+ if k then
+ local u = ucchars[k]
+ local v = rawget(t,u) or rawget(t,shchars[u]) or false
+ t[k] = v
+ return v
+ elseif fallbackself then
+ return k
+ else
+ return false
+ end
+end
+
+table.setmetatableindex(codes,codefallback)
+
+local MorseBetweenWords = context.MorseBetweenWords
+local MorseBetweenCharacters = context.MorseBetweenCharacters
+local MorseLong = context.MorseLong
+local MorseShort = context.MorseShort
+local MorseSpace = context.MorseSpace
+local MorseUnknown = context.MorseUnknown
+
+local function toverbose(str)
+ str = gsub(str,"%s*+%s*","+")
+ str = gsub(str,"%s+"," ")
+ local done = false
+ for m in utfcharacters(str) do
+ if done then
+ MorseBetweenCharacters()
+ end
+ if m == "·" or m == "." then
+ MorseShort()
+ done = true
+ elseif m == "—" or m == "-" then
+ MorseLong()
+ done = true
+ elseif m == " " then
+ if done then
+ MorseBetweenCharacters()
+ end
+ done = false
+ elseif m == "+" then
+ MorseBetweenWords()
+ done = false
+ else
+ MorseUnknown(m)
+ end
+ end
+end
+
+local function toregular(str)
+ local inmorse = false
+ for s in utfcharacters(str) do
+ local m = codes[s]
+ if m then
+ if inmorse then
+ MorseBetweenWords()
+ else
+ inmorse = true
+ end
+ local done = false
+ for m in utfcharacters(m) do
+ if done then
+ MorseBetweenCharacters()
+ else
+ done = true
+ end
+ if m == "·" then
+ MorseShort()
+ elseif m == "—" then
+ MorseLong()
+ elseif m == " " then
+ MorseBetweenCharacters()
+ end
+ end
+ inmorse = true
+ elseif s == "\n" or s == " " then
+ MorseSpace()
+ inmorse = false
+ else
+ if inmorse then
+ MorseBetweenWords()
+ else
+ inmorse = true
+ end
+ MorseUnknown(s)
+ end
+ end
+end
+
+local function tomorse(str,verbose)
+ if verbose then
+ toverbose(str)
+ else
+ toregular(str)
+ end
+end
+
+morse.tomorse = tomorse
+
+function morse.filetomorse(name,verbose)
+ tomorse(resolvers.loadtexfile(name),verbose)
+end
+
+function morse.showtable()
+ context.starttabulate { "|l|l|" } -- { "|l|l|l|" }
+ for k, v in table.sortedpairs(codes) do
+ context.NC() context(k)
+ -- context.NC() context(v)
+ context.NC() tomorse(v,true)
+ context.NC() context.NR()
+ end
+ context.stoptabulate()
+end
+
+\stopluacode
+
+\unprotect
+
+% todo: \setupmorse, but probably it's not worth the trouble.
+
+\def\MorseWidth {0.4em}
+\def\MorseHeight {0.2em}
+%def\MorseShort {\dontleavehmode\blackrule[\c!height=\MorseHeight,\c!width=\dimexpr\MorseWidth]}
+%def\MorseLong {\dontleavehmode\blackrule[\c!height=\MorseHeight,\c!width=3\dimexpr\MorseWidth]}
+\def\MorseShort {\dontleavehmode\vrule\!!width \dimexpr\MorseWidth\!!height\MorseHeight\!!depth\zeropoint\relax}
+\def\MorseLong {\dontleavehmode\vrule\!!width3\dimexpr\MorseWidth\!!height\MorseHeight\!!depth\zeropoint\relax}
+\def\MorseBetweenCharacters {\kern\MorseWidth}
+\def\MorseBetweenWords {\hskip3\dimexpr\MorseWidth\relax}
+\def\MorseSpace {\hskip7\dimexpr\MorseWidth\relax}
+\def\MorseUnknown #text{[\detokenize{#text}]}
+
+\unexpanded\def\MorseCode #text{\ctxlua{moduledata.morse.tomorse(\!!bs#text\!!es,true)}}
+\unexpanded\def\MorseString #text{\ctxlua{moduledata.morse.tomorse(\!!bs#text\!!es)}}
+\unexpanded\def\MorseFile #text{\ctxlua{moduledata.morse.filetomorse("#text")}}
+\unexpanded\def\MorseTable {\ctxlua{moduledata.morse.showtable()}}
+
+\let\Morse \MorseString
+
+%def\MorseShort {·}
+%def\MorseLong {—}
+
+\protect
+
+\continueifinputfile{m-morse.mkvi}
+
+\starttext
+
+\MorseTable
+
+\startlines
+\MorseCode{—·—· ——— —· — · —··— —+—— —·— ·· ···—}
+\MorseCode{—·—· ——— —· — · —··— — + —— —·— ·· ···—}
+\Morse{ÀÁÂÃÄÅàáâãäå}
+\Morse{ÆÇæç}
+\Morse{ÈÉÊËèéêë}
+\Morse{ÌÍÎÏìíîï}
+\Morse{Ññ}
+\Morse{ÒÓÔÕÖòóôõö}
+\Morse{Øø}
+\Morse{ÙÚÛÜùúû}
+\Morse{Ýýÿ}
+\Morse{ß}
+\Morse{Ţţ}
+\stoplines
+
+\Morse{A B C D E F G H I J K L M N O P Q R S T U V W X Y Z}
+
+\stoptext
diff --git a/tex/context/modules/mkiv/m-narrowtt.mkiv b/tex/context/modules/mkiv/m-narrowtt.mkiv
new file mode 100644
index 000000000..7d497a02f
--- /dev/null
+++ b/tex/context/modules/mkiv/m-narrowtt.mkiv
@@ -0,0 +1,39 @@
+%D \module
+%D [ file=m-narrowtt,
+%D version=2005.09.08,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Narrow Verbatim,
+%D author={Hans Hagen \& Ton Otten},
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Test file
+%D
+%D \starttyping
+%D \startTEX
+%D \usemodule[narrowtt]
+%D \starttext
+%D \starttyping
+%D Test test test.
+%D \stoptyping
+%D test \type {test} test \type{test} test
+%D \starttyping
+%D Test test test.
+%D \stoptyping
+%D \stoptext
+%D \stopTEX
+
+\unprotect
+
+\definetypeface
+ [narrowtt] [tt]
+ [mono] [modern-condensed] [\s!default] [\s!features=\s!none]
+
+\definetyping[n\v!typing] \setuptyping[n\v!typing][style=\narrowtt]
+\definetype [n\v!type] \setuptype [n\v!type] [style=\narrowtt]
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/m-nodechart.lua b/tex/context/modules/mkiv/m-nodechart.lua
new file mode 100644
index 000000000..4f2740ef4
--- /dev/null
+++ b/tex/context/modules/mkiv/m-nodechart.lua
@@ -0,0 +1,177 @@
+if not modules then modules = { } end modules ['m-nodechart'] = {
+ version = 1.001,
+ comment = "companion to m-nodechart.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local format = string.format
+local points = number.nopts
+local ptfactor = number.dimenfactors.pt
+
+local nodecodes = nodes.nodecodes
+local kerncodes = nodes.kerncodes
+local penaltycodes = nodes.penaltycodes
+local gluecodes = nodes.gluecodes
+local whatsitcodes = nodes.whatsitcodes
+
+moduledata.charts = moduledata.charts or { }
+moduledata.charts.nodes = moduledata.charts.nodes or { }
+
+local formatters = { }
+
+-- subtype font char lang left right uchyph components xoffset yoffset width height depth
+
+function formatters.glyph(n,comment)
+ return format("\\doFLOWglyphnode{%s}{%s}{%s}{%s}{U+%05X}",comment,n.subtype,n.font,n.char,n.char)
+end
+
+-- pre post replace
+
+function formatters.disc(n,comment)
+ return format("\\doFLOWdiscnode{%s}{%s}",comment,n.subtype)
+end
+
+-- subtype kern
+
+function formatters.kern(n,comment)
+ -- return format("\\doFLOWkernnode{%s}{%s}{%s}",comment,kerncodes[n.subtype],points(n.kern))
+ return format("\\doFLOWkernnode{%s}{%s}{%.4f}",comment,kerncodes[n.subtype],n.kern*ptfactor)
+end
+
+-- subtype penalty
+
+function formatters.penalty(n,comment)
+ return format("\\doFLOWpenaltynode{%s}{%s}{%s}",comment,"penalty",n.penalty)
+end
+
+-- subtype width leader spec (stretch shrink ...
+
+function formatters.glue(n,comment)
+ local s = n.spec
+ -- return format("\\doFLOWgluenode{%s}{%s}{%s}{%s}{%s}",comment,gluecodes[n.subtype],points(s.width),points(s.stretch),points(s.shrink))
+ return format("\\doFLOWgluenode{%s}{%s}{%.4f}{%.4f}{%.4f}",comment,gluecodes[n.subtype],s.width*ptfactor,s.stretch*ptfactor,s.shrink*ptfactor)
+end
+
+-- subtype width leader spec (stretch shrink ...
+
+function formatters.whatsit(n,comment)
+ return whatsitcodes[n.id] or "unknown whatsit"
+end
+
+function formatters.dir(n,comment)
+ return format("\\doFLOWdirnode{%s}{%s}{%s}",comment,"dir",n.dir)
+end
+
+function formatters.localpar(n,comment)
+ return format("\\doFLOWdirnode{%s}{%s}{%s}",comment,"localpar",n.dir)
+end
+
+-- I will make a dedicated set of shapes for this.
+
+local shapes = {
+ glyph = "procedure",
+ disc = "procedure",
+ kern = "action",
+ penalty = "action",
+ glue = "action",
+}
+
+local function flow_nodes_to_chart(specification)
+ local head = specification.head
+ local box = specification.box
+ local comment = specification.comment or ""
+ local x = specification.x or 1
+ local y = specification.y or 0
+ --
+ if box then
+ box = tex.getbox(tonumber(box))
+ head = box and box.list
+ end
+ --
+ local current = head
+ --
+ while current do
+ local nodecode = nodecodes[current.id]
+ local formatter = formatters[nodecode]
+ local shape = shapes[nodecode]
+ y = y + 1
+ local next = current.next
+ commands.flow_start_cell { shape = { framecolor = "nodechart:" .. nodecode } }
+ commands.flow_set_name(tostring(current))
+ commands.flow_set_location(x,y)
+ if shape then
+ commands.flow_set_shape(shape)
+ end
+ if formatter then
+ commands.flow_set_text("node",formatter(current,comment))
+ else
+ commands.flow_set_text("node",nodecode)
+ end
+ if next then
+ commands.flow_set_connection("bt","",tostring(next))
+ end
+ if nodecode == "glyph" then
+ local components = current.components
+ if components then
+ commands.flow_set_connection("rl","",tostring(components))
+ commands.flow_stop_cell()
+ n = flow_nodes_to_chart { head = components, comment = "component",x = x+2, y = y-1 }
+ else
+ commands.flow_stop_cell()
+ end
+ elseif nodecode == "disc" then
+ local pre = current.pre
+ local pos = current.post
+ local rep = current.replace
+ if pre and not rep and not rep then
+ if pre then
+ commands.flow_set_connection("rl","",tostring(pre))
+ end
+ commands.flow_stop_cell()
+ if pre then
+ n = flow_nodes_to_chart { head = pre, comment = "prebreak", x = x+1, y = y-1 }
+ end
+ else
+ if pre then
+ commands.flow_set_connection("+rl","",tostring(pre))
+ end
+ if rep then
+ commands.flow_set_connection("rl","",tostring(rep))
+ end
+ if pos then
+ commands.flow_set_connection("-rl","",tostring(pos))
+ end
+ commands.flow_stop_cell()
+ if pre then
+ n = flow_nodes_to_chart{ head = pre, comment = "prebreak", x = x+1, y = y-2 }
+ end
+ if rep then
+ n = flow_nodes_to_chart{ head = rep, comment = "replacement", x = x+1, y = y-1 }
+ end
+ if pos then
+ n = flow_nodes_to_chart{ head = pos, comment = "postbreak", x = x+1, y = y }
+ end
+ end
+ elseif nodecode == "hlist" then
+ local list = current.list
+ if list then
+ commands.flow_set_connection("rl","",tostring(list))
+ commands.flow_stop_cell()
+ n = flow_nodes_to_chart { head = list, comment = "list", x = x+2, y = y-1 }
+ else
+ commands.flow_stop_cell()
+ end
+ else
+ commands.flow_stop_cell()
+ end
+ current = next
+ end
+end
+
+function moduledata.charts.nodes.chart(specification)
+ commands.flow_start_chart(specification.name)
+ flow_nodes_to_chart(specification)
+ commands.flow_stop_chart()
+end
diff --git a/tex/context/modules/mkiv/m-nodechart.mkvi b/tex/context/modules/mkiv/m-nodechart.mkvi
new file mode 100644
index 000000000..c9d985850
--- /dev/null
+++ b/tex/context/modules/mkiv/m-nodechart.mkvi
@@ -0,0 +1,125 @@
+%D \module
+%D [ file=m-nodechart,
+%D version=2011.11.11, % nos sure when it started, needed for fonts-mkiv
+%D title=\CONTEXT\ Modules,
+%D subtitle=Node Visualization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{m-nodechart}{1.001}
+
+\usemodule[chart]
+
+\unprotect
+
+\def\enspaceminus{\hskip.5em minus .25em\relax}
+
+\starttexdefinition unexpanded doFLOWglyphnode #comment #subtype #font #char #unicode
+ \dontleavehmode\hbox{\bf\setstrut\strut \doifsomething{#comment}{#comment\enspaceminus:\enspaceminus}glyph #subtype}
+ \vss
+ \dontleavehmode\hbox{\tx\setstrut\strut font\enspaceminus#font:\enspace#unicode:\enspaceminus\setfontofid{#font}\char#char}
+\stoptexdefinition
+
+\starttexdefinition unexpanded doFLOWdiscnode #comment #subtype
+ \dontleavehmode\hbox{\bf\setstrut\strut disc}
+ \vss
+ \dontleavehmode\hbox{\tx\setstrut\strut}
+\stoptexdefinition
+
+\starttexdefinition unexpanded doFLOWkernnode #comment #subtype #kern
+ \dontleavehmode\hbox{\bf\setstrut\strut#subtype}
+ \vss
+ \dontleavehmode\hbox{\tx\setstrut\strut#kern}
+\stoptexdefinition
+
+\starttexdefinition unexpanded doFLOWpenaltynode #comment #subtype #penalty
+ \dontleavehmode\hbox{\bf\setstrut\strut#subtype}
+ \vss
+ \dontleavehmode\hbox{\tx\setstrut\strut#penalty}
+\stoptexdefinition
+
+\starttexdefinition unexpanded doFLOWgluenode #comment #subtype #width #shrink #stretch
+ \dontleavehmode\hbox{\bf\setstrut\strut#subtype}
+ \vss
+ \dontleavehmode\hbox{\tx\setstrut\strut#width\enspaceminus-\enspaceminus#shrink\enspaceminus+\enspaceminus#stretch}
+\stoptexdefinition
+
+\starttexdefinition unexpanded doFLOWdirnode #comment #subtype #direction
+ \dontleavehmode\hbox{\bf\setstrut\strut#subtype}
+ \vss
+ \dontleavehmode\hbox{\tx\setstrut\strut#direction}
+\stoptexdefinition
+
+\defineframed
+ [flowcell:node]
+ [flowcell:base]
+ [\c!top=\vss,
+ \c!bottom=\vss,
+ \c!align=\v!middle,
+ \c!foregroundstyle=\tt]
+
+% this is a temporary interface ... we will have instances and optional settings
+
+\unexpanded\def\boxtoFLOWchart[#name]#box%
+ {\ctxlua{moduledata.charts.nodes.chart {
+ name = "#name",
+ box = \number#box,
+ }}}
+
+\unexpanded\def\nextboxtoFLOWchart[#name]%
+ {\dowithnextbox{\boxtoFLOWchart[#name]\nextbox}}
+
+\unexpanded\def\hboxtoFLOWchart[#name]%
+ {\nextboxtoFLOWchart[#name]\hbox}
+
+\unexpanded\def\vboxtoFLOWchart[#name]%
+ {\nextboxtoFLOWchart[#name]\vbox}
+
+\protect
+
+\continueifinputfile{m-nodechart.mkvi}
+
+\definecolor[nodechart:glyph][darkred]
+
+\setupbodyfont[dejavu,10pt]
+
+\starttext
+
+\startTEXpage[offset=10pt]
+
+ \hboxtoFLOWchart[dummy]{an affil\discretionary{-}{-}{!}iation}
+
+ \FLOWchart[dummy][width=14em,height=3em,dx=1em,dy=.75em,hcompact=yes]
+
+\stopTEXpage
+
+\startTEXpage[offset=10pt]
+
+ \hboxtoFLOWchart[dummy]{an affiliation}
+
+ \FLOWchart[dummy][width=14em,height=3em,dx=.5em,dy=.75em,hcompact=yes]
+
+\stopTEXpage
+
+\startTEXpage[offset=10pt]
+
+ \hboxtoFLOWchart[dummy]{\nl effe fijn fietsen}
+
+ \FLOWchart[dummy][width=14em,height=3em,dx=.5em,dy=.75em,hcompact=yes]
+
+\stopTEXpage
+
+\startTEXpage[offset=10pt]
+
+ \hboxtoFLOWchart[dummy]{\righttoleft t\kern 1pt est}
+
+ \FLOWchart[dummy][width=14em,height=3em,dx=.5em,dy=.75em,hcompact=yes]
+
+\stopTEXpage
+
+\stoptext
diff --git a/tex/context/modules/mkiv/m-ntb-to-xtb.mkiv b/tex/context/modules/mkiv/m-ntb-to-xtb.mkiv
new file mode 100644
index 000000000..55b4ab259
--- /dev/null
+++ b/tex/context/modules/mkiv/m-ntb-to-xtb.mkiv
@@ -0,0 +1,5 @@
+\loadmkvifile{tabl-xnt}
+
+\mapTABLEtoxtable
+
+\endinput
diff --git a/tex/context/modules/mkiv/m-obsolete.mkiv b/tex/context/modules/mkiv/m-obsolete.mkiv
new file mode 100644
index 000000000..2d4518181
--- /dev/null
+++ b/tex/context/modules/mkiv/m-obsolete.mkiv
@@ -0,0 +1,5 @@
+\unprotect
+
+\writestatus\m!system{skipping obsolete module}
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/m-oldfun.mkiv b/tex/context/modules/mkiv/m-oldfun.mkiv
new file mode 100644
index 000000000..3f2ec0263
--- /dev/null
+++ b/tex/context/modules/mkiv/m-oldfun.mkiv
@@ -0,0 +1,714 @@
+%D \module
+%D [ file=m-oldfun, % was: supp-fun
+%D version=1995.10.10,
+%D title=\CONTEXT\ Support Macros,
+%D subtitle=Fun Stuff,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+%D Beware, these macros wil be replaced and at some point this
+%D module will not be preloaded any more.
+
+%D This module implements some typographics tricks that can
+%D be fun when designing document layouts. The examples use
+%D macros that are typical to \CONTEXT, but non \CONTEXT\
+%D users can use the drop caps and first line treatment
+%D macros without problems. This module will be extended
+%D when the need for more of such tricks arises.
+
+\writestatus{loading}{ConTeXt Support Macros / Fun Stuff}
+
+%D \macros
+%D {DroppedCaps, DroppedString, DroppedIndent, DroppedLines}
+%D
+%D \startbuffer
+%D \DroppedCaps
+%D {\color[green]} {SerifBold}
+%D {\the\dimexpr2.2\baselineskip} {2pt} {\the\baselineskip} {2}
+%D Let's start
+%D \stopbuffer
+%D
+%D \getbuffer with dropped caps, those blown up first
+%D characters of a paragraph. It's hard to implement a general
+%D mechanism that suits all situations, but dropped caps are so
+%D seldomly used that we can permit ourselves a rather user
+%D unfriendly implementation.
+%D
+%D \typebuffer
+%D
+%D As we will see, there are 7 different settings involved. The
+%D first argument takes a command that is used to do whatever
+%D fancy things we want to do, but normally this one will be
+%D empty. The second argument takes the font. Because we're
+%D dealing with something very typographic, there is no real
+%D reason to adopt complicated font switching schemes, a mere
+%D name will do. Font encodings can bring no harm, because the
+%D alphanumeric characters are nearly always located at their
+%D natural position in the encoding vector.
+%D
+%D \startbuffer
+%D \DroppedCaps
+%D {\color[red]} {SerifBold}
+%D {\the\baselineskip} {0pt} {0pt} {1}
+%D This simple
+%D \stopbuffer
+%D
+%D \getbuffer case shows us what happens when we apply minimal
+%D values. Here we used:
+%D
+%D \typebuffer
+%D
+%D \startbuffer
+%D \DroppedCaps
+%D {\color[red]} {SerifBold}
+%D {\the\dimexpr2\baselineskip} {0pt} {\the\baselineskip} {2}
+%D Is this ugly
+%D \stopbuffer
+%D
+%D \getbuffer example the third argument tells
+%D this macro that we want a dropped capital scaled to the
+%D baseline distance. The two zero point arguments are the
+%D horizontal and vertical offsets and the last arguments
+%D determines the hanging indentation. In this paragraph we
+%D set the height to two times the baselinedistance and use
+%D two hanging lines:
+%D
+%D \typebuffer
+%D
+%D Here, the first character is moved down one baseline. Here
+%D we also see why the horizontal offset is important. The
+%D first example (showing the~L) sets this to a few points and
+%D also used a slightly larger height.
+%D
+%D Of course common users (typist) are not supposed to see this
+%D kind of fuzzy definitions, but fortunately \TEX\ permits us
+%D to hide them in macros. Using a macro also enables us to
+%D garantee consistency throughout the document:
+%D
+%D \startbuffer
+%D \def\MyDroppedCaps%
+%D {\DroppedCaps
+%D {\color[green]} {SerifBold}
+%D {\the\dimexpr5\baselineskip} {3pt} {\the\dimexpr3\baselineskip} {4}}
+%D
+%D \MyDroppedCaps The implementation
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \getbuffer of the general macro is rather simple and only
+%D depends on the arguments given and the dimensions of the
+%D strut box. We explicitly load the font, which is no problem
+%D because \TEX\ does not load a font twice. We could have
+%D combined some arguments, like the height, vertical offset
+%D and the number of lines, but the current implementation
+%D proved to be the most flexible. One should be aware of the
+%D fact that the offsets depend on the design of the glyphs
+%D used.
+
+\let\DroppedIndent\!!zeropoint \def\DroppedLines{0}
+
+\def\DroppedString{ABCDEFGHIJKLMNOPQRSTUVWXYZ}
+
+\let\globaldropcaps\global % will be an option, but on by default
+
+\unexpanded\def\localdropcaps{\let\globaldropcaps\relax}
+
+\chardef\DroppedStatus = 0 % 0=done 1=starting 2=doing 3=error
+\chardef\DropMode = 0 % 1 == marginhang
+
+\ifx\keeplinestogether\undefined
+ \let\keeplinestogether\gobbleoneargument
+\fi
+
+\unexpanded\def\DroppedCaps#1#2#3#4#5#6#7% does not yet handle accented chars
+ {\defconvertedargument\asciia{#7}%
+ \defconvertedcommand \asciib{\DroppedString}%
+ \doifelseinstring\asciia\asciib
+ {\noindentation
+ \dontleavehmode
+ \checkindentation % redo this one
+ %\ifhmode\hskip-\parindent\fi % sensitive for context mechanism
+ \keeplinestogether{#6}%
+ \setbox0\hbox{\definedfont[#2 at #3]#1{#7}\hskip#4}%
+ \ifdim\dp0>\strutdp % one of those Q's , will be option
+ \setbox2\hbox{\raise\dp0\hbox{\lower\strutdp\copy0}}%
+ \ht2\ht0
+ \dp0\strutdp
+ \setbox0\box2
+ \fi
+ \setbox0\hbox
+ {\ifnum\DropMode=\plusone
+ \hskip-\wd0\wd0\zeropoint
+ \fi
+ \lower#5\box0}%
+ \ht0\strutht
+ \dp0\strutdp
+ \ifnum\DropMode=\plusone
+ \globaldropcaps\let\DroppedIndent\!!zeropoint
+ \globaldropcaps\edef\DroppedLines{\number\maxdimen}%
+ \globaldropcaps\chardef\DroppedStatus\plusthree
+ \else
+ \globaldropcaps\edef\DroppedIndent{\the\wd0}%
+ \globaldropcaps\edef\DroppedLines {\number#6}%
+ \globaldropcaps\chardef\DroppedStatus\plustwo
+ \globaldropcaps\hangindent\DroppedIndent
+ \globaldropcaps\hangafter-\DroppedLines
+% \noindent
+ \noindentation
+ \checkindentation % redo this one
+ \hskip-\DroppedIndent
+ \fi
+ \vbox{\forgetall\box0}%
+ \nobreak
+ \let\next\ignorespaces} % Could be a one character word !
+ {\globaldropcaps\let\DroppedIndent\!!zeropoint
+ \globaldropcaps\edef\DroppedLines{\number\maxdimen}%
+ \globaldropcaps\chardef\DroppedStatus\plusthree
+ \def\next{#7}}%
+ \let\globaldropcaps\global
+ \next}
+
+%D Before we go to the next topic, we summarize this command:
+%D
+%D \starttyping
+%D \DroppedCaps
+%D {command} {font}
+%D {height} {hoffset} {voffset} {lines}
+%D \stoptyping
+%D
+%D Sometimes you need to make sure that the global settings are
+%D kept local, as in:
+%D
+% %D \startbuffer
+% %D \defineparagraphs[SomePar][n=2,rule=on]
+% %D \setupparagraphs [SomePar][1][width=.5\textwidth]
+% %D \setupparagraphs [SomePar][2][width=.5\textwidth]
+%D \startbuffer
+%D \defineparagraphs[SomePar][n=2,rule=on]
+%D \setupparagraphs [SomePar][1][width=.5\textwidth]
+%D \setupparagraphs [SomePar][2][width=.5\textwidth]
+%D
+%D \startSomePar
+%D \localdropcaps\NiceDroppedCaps{}{cmr12}{0pt}{2}Here we need
+%D to explicitly keep the hanging indentation local, like it or
+%D not.
+%D \SomePar
+%D \localdropcaps\NiceDroppedCaps{}{cmr12}{0pt}{2}Here we need
+%D to explicitly keep the hanging indentation local, like it or
+%D not.
+%D \stopSomePar
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+%D \macros
+%D {AutoDroppedCaps, CheckDroppedCaps}
+%D
+%D {\em To be documented.}
+
+% example usage
+%
+% \def\bpar{\ifvmode\CheckDroppedCaps\fi}
+% \def\epar{\ifhmode\par\fi\CheckDroppedCaps}
+
+\newcount\lastprevgraf
+\newcount\droppedlines
+
+\unexpanded\def\CheckDroppedCaps
+ {\global\lastprevgraf\prevgraf}
+
+\unexpanded\def\AutoDroppedCaps % will be proper core stuff since it
+ {\globaldropcaps\chardef\DroppedStatus\plusone
+ \global\lastprevgraf\zerocount
+ \global\droppedlines\zerocount
+ \EveryPar{\doAutoDroppedCaps}}
+
+\let\AutoDroppedNext\relax
+
+\ifx\AutoDroppedCapsCommand\undefined
+ \unexpanded\def\AutoDroppedCapsCommand{\NiceDroppedCaps{}{SerifBold}{.125em}{3}}
+\fi
+
+\unexpanded\def\doAutoDroppedCaps
+ {\ifcase\DroppedStatus % done
+ \let\next\relax
+ \or % starting
+ % \ifnum\lastprevgraf>0 % tricky, probably a wrong par
+ % \globaldropcaps\chardef\DroppedStatus=3 % and inhibits dropped
+ % \let\next\relax % caps after titles and more than once
+ % \else % so let's nill this rubishly code fragment
+ \let\next\AutoDroppedCapsCommand
+ % \fi % and hope for the best
+ \or % doing
+ \global\advance\droppedlines \lastprevgraf
+ \ifnum\droppedlines=\zerocount
+ \globaldropcaps\chardef\DroppedStatus\zerocount
+ \let\next\relax
+ \else\ifnum\droppedlines>\zerocount
+ \ifnum\droppedlines<\DroppedLines\relax
+ \globaldropcaps\hangindent\DroppedIndent
+ \globaldropcaps\hangafter-\DroppedLines
+ \globaldropcaps\advance\hangafter \droppedlines
+ \hskip-\parindent % brrr
+ \let\next\AutoDroppedNext
+ \else
+ \globaldropcaps\chardef\DroppedStatus\zerocount
+ \let\next\relax
+ \fi
+ \else
+ \globaldropcaps\chardef\DroppedStatus\zerocount
+ \let\next\relax
+ \fi\fi
+ \or % error
+ \globaldropcaps\chardef\DroppedStatus\zerocount
+ \let\next\relax
+ \fi
+ \next}
+
+%D \macros
+%D {LineDroppedCaps, NiceDroppedCaps}
+%D
+%D To save definitions, we also provide:
+%D
+%D \starttyping
+%D \LineDroppedCaps {command} {font} {hoffset} {lines}
+%D \NiceDroppedCaps {command} {font} {hoffset} {lines}
+%D \stoptyping
+%D
+%D The first command scales the font to the exact height, while
+%D the second command scales the font to a nice 2.5 times the
+%D line height, a value that gives a pleasant grayness.
+
+\unexpanded\def\DoLineDroppedCaps#1#2#3#4#5% compensation command font offset lines
+ {\scratchcounter#5%
+ \advance\scratchcounter \minusone
+ \scratchdimen\scratchcounter\baselineskip
+ \advance\scratchdimen #1%
+ \NormalizeFontHeight\DummyFont{W}\scratchdimen{#3}%
+ \DroppedCaps{#2}{#3}\TheNormalizedFontSize{#4}
+ {\scratchcounter\baselineskip}{#5}}
+
+\unexpanded\def\LineDroppedCaps% command font offset lines
+ {\DoLineDroppedCaps{\strutht}}
+
+\unexpanded\def\NiceDroppedCaps% command font offset lines
+ {\DoLineDroppedCaps{.5\baselineskip}}
+
+%D \macros
+%D {TreatFirstLine}
+%D
+%D \startbuffer
+%D \TreatFirstLine {\sc} {} {} {}
+%D Instead of limiting its action to one token, the next macro
+%D treats the whole first line. This paragraph was typeset by
+%D saying:
+%D \stopbuffer
+%D
+%D \getbuffer
+%D
+%D \typebuffer
+%D
+%D \startbuffer
+%D \TreatFirstLine {\startcolor[red]\bf} {\stopcolor} {} {}
+%D The combined color and font effect is also possible,
+%D although one must be careful in using macros that accumulate
+%D grouping, but the commands used here are pretty save in that
+%D respect.
+%D \stopbuffer
+%D
+%D \getbuffer
+%D
+%D \typebuffer
+%D
+%D Before we explain the third and fourth argument, we show the
+%D implementation. Those who know a bit about the way \TEX\
+%D treats tokens, will probably see in one glance that this
+%D alternative works all right for most text||only situations
+%D in which there is enough text available for the first line,
+%D but that more complicated things will blow. One has to live
+%D with that. A workaround is rather trivial but obscures the
+%D principles used.
+
+\unexpanded\def\TreatFirstLine#1#2#3#4% before, after, first, next
+ {\leavevmode
+ \bgroup
+ \forgetall
+ \bgroup
+ #1%
+ \setbox0\emptybox
+ \setbox2\emptybox
+ \def\grabfirstline##1 %
+ {\setbox2\hbox
+ {\ifvoid0
+ {#3{\ignorespaces##1}}%
+ \else
+ \unhcopy0\ {#4{##1}}%
+ \fi}%
+ \ifdim\wd2=\zeropoint
+ \setbox0\emptybox
+ \setbox2\emptybox
+ \@EA\grabfirstline
+ \else\ifdim\wd2>\hsize
+ \hbox to \hsize{\strut\unhbox0}#2\egroup
+ \break##1\
+ \egroup
+ \else
+ \setbox0\box2
+ \@EAEAEA\grabfirstline
+ \fi\fi}%
+ \grabfirstline}
+
+%D \startbuffer
+%D \gdef\FunnyCommand
+%D {\getrandomfloat\FunnyR{0}{1}%
+%D \getrandomfloat\FunnyG{0}{1}%
+%D \getrandomfloat\FunnyB{0}{1}%
+%D \definecolor[FunnyColor][r=\FunnyR,g=\FunnyG,b=\FunnyB]%
+%D \color[FunnyColor]}
+%D
+%D %\TreatFirstLine {\bf} {} {\FunnyCommand} {\FunnyCommand}
+%D The third and fourth argument can be used to gain special
+%D effects on the individual words. Of course one needs ...
+%D \stopbuffer
+%D
+%D \getbuffer
+%D to know a bit more about the macro package used to get real
+%D nice effects, but this example probably demonstrates the
+%D principles well.
+%D
+%D \typebuffer
+%D
+%D Like in dropped caps case, one can hide such treatments in a
+%D macro, like:
+%D
+%D \starttyping
+%D \def\MyTreatFirstLine%
+%D {\TreatFirstLine{\bf}{}{\FunnyCommand}{\FunnyCommand}}
+%D \stoptyping
+
+%D \macros
+%D {reshapebox}
+%D
+%D \startbuffer
+%D \beginofshapebox
+%D When using \CONTEXT, one can also apply this funny command
+%D to whole lines by using the reshape mechanism. Describing
+%D this interesting mechanism falls outside the scope of this
+%D module, so we only show the trick. This is an example of
+%D low level \CONTEXT\ functionality: it's all there, and it's
+%D stable, but not entirely meant for novice users.
+%D \endofshapebox
+%D
+%D \reshapebox{\FunnyCommand{\box\shapebox}} \flushshapebox
+%D \stopbuffer
+%D
+%D \getbuffer
+%D
+%D \typebuffer
+%D
+%D This mechanism permits hyphenation and therefore gives
+%D better results than the previously discussed macro
+%D \type{\TreatFirstLine}.
+
+%D \macros
+%D {TreatFirstCharacter}
+%D
+%D \startbuffer
+%D \TreatFirstCharacter{\bf\color[green]} Just to be
+%D \stopbuffer
+%D
+%D \getbuffer complete we also offer a very simple one
+%D character alternative, that is not that hard to understand:
+
+\unexpanded\def\TreatFirstCharacter#1#2% command, character
+ {{#1{#2}}}
+
+%D A previous paragraph started with:
+%D
+%D \typebuffer
+
+%D \macros
+%D {StackCharacters}
+%D
+%D The next hack deals with vertical stacking.
+
+\unexpanded\def\StackCharacters#1#2#3#4% sequence vsize vskip command
+ {\vbox #2
+ {\forgetall
+ \baselineskip\zeropoint
+ \def\StackCharacter##1{#4{##1}\cr\noalign{#3}}%
+ \halign
+ {\hss##\hss&##\cr
+ \handletokens#1\with\StackCharacter\cr}}}
+
+%D \startbuffer
+%D \StackCharacters{CONTEXT}{}{\vskip.2ex}{\FunnyCommand}
+%D \stopbuffer
+%D
+%D Such a stack looks like:
+%D
+%D \startlinecorrection
+%D \hbox to \hsize
+%D {$\hss\bfd
+%D \vcenter{\StackCharacters{TEX} {}{\vskip.2ex}{\FunnyCommand}}%
+%D \hss
+%D \vcenter{\StackCharacters{CON} {}{\vskip.2ex}{\FunnyCommand}}
+%D \hss
+%D \vcenter{\StackCharacters{TEXT} {}{\vskip.2ex}{\FunnyCommand}}
+%D \hss
+%D \vcenter{\StackCharacters{CONTEXT}{}{\vskip.2ex}{\FunnyCommand}}
+%D \hss$}
+%D \stoplinecorrection
+%D
+%D and is typeset by saying:
+%D
+%D \typebuffer
+%D
+%D An alternative would have been
+%D
+%D \starttyping
+%D \StackCharacters {CONTEXT} {to 5cm} {\vfill} {\FunnyCommand}
+%D \stoptyping
+
+%D \macros
+%D {processtokens}
+%D
+%D At a lower level horizontal and vertical manipulations are
+%D already supported by:
+%D
+%D \starttyping
+%D \processtokens {begin} {between} {end} {space} {text}
+%D \stoptyping
+%D
+%D \startbuffer[a]
+%D \processtokens
+%D {\hbox to .5\hsize\bgroup} {\hfill}
+%D {\egroup} {\space} {LET'S HAVE}
+%D \stopbuffer
+%D
+%D \startbuffer[b]
+%D \processtokens
+%D {\vbox\bgroup\raggedcenter\hsize1em}
+%D {\vskip.25ex} {\egroup} {\strut} {FUN}
+%D \stopbuffer
+%D
+%D This macro is able to typeset:
+%D
+%D \leavevmode\hbox to \hsize
+%D {$\hfil\hfil
+%D \vcenter{\bf\getbuffer[a]}%
+%D \hfil
+%D \vcenter{\bfd\getbuffer[b]}%
+%D \hfil\hfil$}
+%D
+%D which was specified as:
+%D
+%D \typebuffer[a]
+%D \typebuffer[b]
+
+%D \macros
+%D {NormalizeFontHeight, NormalizeFontWidth,
+%D TheNormalizedFontSize}
+%D
+%D Next we introduce some font manipulation macros. When we
+%D want to typeset some text spread in a well defined area, it
+%D can be considered bad practice to manipulate character and
+%D word spacing. In such situations the next few macros can be
+%D of help:
+%D
+%D \starttyping
+%D \NormalizeFontHeight \name {sample text} {height} {font}
+%D \NormalizeFontWidth \name {sample text} {width} {font}
+%D \stoptyping
+%D
+%D These are implemented using an auxilliary macro:
+
+\unexpanded\def\NormalizeFontHeight{\NormalizeFontSize\ht}
+\unexpanded\def\NormalizeFontWidth {\NormalizeFontSize\wd}
+
+\unexpanded\def\NormalizeFontSize#1#2#3#4#5%
+ {\bgroup
+ \dimen0=#4% #4 can be \ht0 or so
+ \setbox0\hbox{\definedfont[#5 at 5pt]#3}% 10pt
+ \ifdim\wd0>\zeropoint
+ \dimen2=#10 % #1 is \wd or \ht
+ \dimen4=\maxdimen % 10000pt
+ \divide\dimen4 \dimen2
+ \divide\dimen0 1638 % 1000
+ \dimen0=\number\dimen4\dimen0
+ \divide \dimen0 \plustwo % ...
+ \xdef\TheNormalizedFontSize{\the\dimen0}%
+ \else
+ \dimen0\bodyfontsize
+ \fi
+ \normalexpanded{\egroup\def\noexpand#2{\definedfont[#5 at \the\dimen0]}}}
+
+%D Afterwards, we have access to the calculated size by:
+
+\let\TheNormalizedFontSize\!!zeropoint
+
+%D Extra:
+
+\unexpanded\def\WidthSpanningText#1#2#3% text width font
+ {\hbox{\NormalizeFontWidth\temp{#1}{#2}{#3}\temp\the\everydefinedfont#1}}
+
+%D Consider for instance:
+%D
+%D \startbuffer
+%D \NormalizeFontHeight \tmp {X} {2\baselineskip} {cmr10}
+%D
+%D {\tmp To Be Or Not To Be}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D This shows up as (we also show the baselines):
+%D
+%D {\showbaselines\getbuffer}
+%D
+%D The horizontal counterpart is:
+%D
+%D \startbuffer
+%D \NormalizeFontWidth \tmp {This Line Fits} {\hsize} {cmr10}
+%D
+%D \hbox{\tmp This Line Fits}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D The calculated font scale is avaliable in the macro
+%D \type{\NormalizedFontSize}.
+%D
+%D \startlinecorrection
+%D \ruledhbox{\getbuffer}
+%D \stoplinecorrection
+%D
+%D One can of course combine these macros with the ones
+%D described earlier, like in:
+%D
+%D \starttyping
+%D \NormalizeFontHeight {text} \DroppedFont {2\baselineskip} {cmbx12}
+%D
+%D \def\NicelyDroppedCaps
+%D {\DroppedCaps
+%D {\color[green]}
+%D {\DroppedFont}
+%D {2pt}
+%D {\baselineskip}
+%D {2}}
+%D \stoptyping
+%D
+%D It's up to the reader to test this one.
+
+\unexpanded\def\FirstNCharacters#1#2% \FirstNCharacters{3}{fr{\"o}beln}
+ {\bgroup
+ \scratchcounter\zerocount
+ \def\docommand##1%
+ {\ifnum\scratchcounter=#1\else
+ ##1\relax % catches ##1 = \"e and alike
+ \advance\scratchcounter\plusone
+ \fi}
+ \handletokens#2\with\docommand
+ \egroup}
+
+%D \macros
+%D {FittingText}
+%D
+%D First used in Pascal (demo-bbv):
+%D
+%D \startbuffer
+%D \ruledvbox{\FittingText{3cm}{1cm}{Serif}{24pt}{1pt}{1}
+%D {\veryraggedright
+%D \hangindent1em\hangafter1\relax
+%D \begstrut \dorecurse{8}{Bram Marta }\unskip \endstrut}}
+%D
+%D \ruledvbox{\FittingText{3cm}{1cm}{Serif}{24pt}{1pt}{1}
+%D {\raggedleft\begstrut Bram\\Marta \unskip\endstrut}}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlinecorrection
+%D \getbuffer
+%D \stoplinecorrection
+
+% #1 width #2 height #3 font #4 size #5 step #6 interlinie #7 text
+
+\unexpanded\def\FittingText#1#2#3#4#5#6#7%
+ {\bgroup
+ \forgetall
+ \dontcomplain
+ \setuptolerance[\v!verytolerant]% == \tolerance4500
+ \hsize#1%
+ \def\\{\softbreak}%
+ \!!heighta#4%
+ \!!heightb#2%
+ \doloop
+ {\ifdim\!!heighta>\onepoint
+ \expanded{\definefont[\s!dummy][#3 at \the\!!heighta][\c!interlinespace=#6]}%
+ \getvalue\s!dummy
+ \setbox\scratchbox\vbox{#7\endgraf}%
+ \ifdim\ht\scratchbox>\!!heightb
+ \advance\!!heighta-#5%
+ \else
+ \beginshapebox
+ \unvcopy\scratchbox
+ \endshapebox
+ \global\dimen1\hsize
+ \reshapebox
+ {\setbox\shapebox\hbox{\unhbox\shapebox}%
+ \ifdim\wd\shapebox>\dimen1
+ \global\dimen1\wd\shapebox
+ \fi}%
+ \ifdim\dimen1>\hsize
+ \advance\!!heighta-#5%
+ \else
+ \exitloop
+ \fi
+ \fi
+ \else
+ \exitloop
+ \fi}%
+ %\writestatus{\strippedcsname\FittingText}{height: \the\!!heighta}%
+ \unvbox\scratchbox
+ \egroup}
+
+% \font width gap font spec text
+
+\unexpanded\def\NormalizeFontWidthSpread#1#2#3#4#5#6%
+ {\global\setfalse\NFSpread
+ \scratchdimen#3%
+ \scratchdimen-.5\scratchdimen
+ \advance\scratchdimen#2\relax
+ \NormalizeFontWidth
+ #1%
+ {\def\+{\global\settrue\NFSpread\gobbleuntil\relax}%
+ \def\\{\gobbleuntil\relax}% newline
+ \setupspacing
+ #6\relax}%
+ {\scratchdimen}%
+ {#4}%
+ \ifconditional\NFSpread
+ % de gap valt in de binding
+ \else
+ \definefont[\strippedcsname#1][#4 #5]%
+ \fi}
+
+\unexpanded\def\SpreadGapText#1#2%
+ {{\def\+{\kern#1}#2}}
+
+\unexpanded\def\GapText#1#2#3#4#5% width distance font spec title
+ {\bgroup
+ \NormalizeFontWidthSpread\DummyFont{#1}{#2}{#3}{#4}{#5}%
+ \DummyFont\setupspacing\SpreadGapText{#2}{#5}\endgraf
+ \egroup}
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/m-oldnum.mkiv b/tex/context/modules/mkiv/m-oldnum.mkiv
new file mode 100644
index 000000000..382c56eb6
--- /dev/null
+++ b/tex/context/modules/mkiv/m-oldnum.mkiv
@@ -0,0 +1,416 @@
+%D \module
+%D [ file=m-oldnum, % was: supp-num
+%D version=1998.05.15,
+%D title=\CONTEXT\ Support Macros,
+%D subtitle=Numbers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% See end for transition to mkiv.
+
+\writestatus{loading}{ConTeXt Support Macros / Numbers}
+
+\unprotect
+
+%D \macros
+%D {digits, setdigitmode, setdigitsign}
+%D
+%D Depending on the digit mode the command \type {\digits}
+%D normalizes number patterns depending on the language set.
+%D
+%D \starttyping
+%D This will never be a \digits{1.000.000} seller.
+%D \stoptyping
+%D
+%D or
+%D
+%D \starttyping
+%D I will never grow longer than \digits 1.86 \Meter.
+%D \stoptyping
+%D
+%D The different modes are shown in:
+%D
+%D \startbuffer
+%D \setdigitmode 1 \digits 12.345,90 \digits 12.345.000 \digits 1,23
+%D \setdigitmode 2 \digits 12.345,90 \digits 12.345.000 \digits 1,23
+%D \setdigitmode 3 \digits 12.345,90 \digits 12.345.000 \digits 1,23
+%D \setdigitmode 4 \digits 12.345,90 \digits 12.345.000 \digits 1,23
+%D \setdigitmode 5 \digits 12.345,90 \digits 12.345.000 \digits 1,23
+%D \setdigitmode 6 \digits 12.345,90 \digits 12.345.000 \digits 1,23
+%D \stopbuffer
+%D
+%D \typebuffer
+%
+% This is typset as:
+%
+% \startlines
+% \getbuffer
+% \stoplines
+%D
+%D The sign can be typeset as is or within the space of a
+%D digit.
+%D
+%D \startbuffer
+%D \setdigitsign 0 \digits +12.345,90
+%D \setdigitsign 1 \digits +12.345,90
+%D \setdigitsign 2 \digits +12.345,90
+%D \setdigitsign 3 \digits +12.345,90
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+% This is typset as:
+%
+% \startlines
+% \getbuffer
+% \stoplines
+
+\chardef\digitoutputmode=1 % 0..6
+\chardef\digitsignmode =0 % 0..3
+
+\unexpanded\def\setdigitmode{\chardef\digitoutputmode}
+\unexpanded\def\setdigitsign{\chardef\digitsignmode}
+
+%D The digit modes are:
+%D
+%D \startitemize[packed]
+%D \item periods \& comma
+%D \item commas \& period
+%D \item thinmuskips \& comma
+%D \item thinmuskips \& period
+%D \item thickmuskips \& comma
+%D \item thickmuskips \& period
+%D \stopitemize
+
+\let\collecteddigits \empty \chardef\digitinputmode =1
+\let\saveddigits \empty \chardef\skipdigit =0
+\let\savedpowerdigits\empty \chardef\powerdigits =0
+
+%D The first stage of the \type {\digit} macro takes care of
+%D the grouped call, the other branch handles the fuzzy
+%D delimited calls.
+
+\ifdefined\mbox \else \let\mbox\hbox \fi
+
+\unexpanded\def\digits
+ {\bgroup
+ \let~@%
+ \doifelsenextbgroup\dodigits{\doifelsenextchar\normalmathshift\domathdigits\grabdigit}}
+
+\def\dodigits#1%
+ {\grabdigit#1\relax}
+
+\def\domathdigits$#1$%
+ {\mbox{\grabdigit#1\relax}} % adding $ $ goes wrong in tabulate
+
+\def\grabdigit
+ {\futurelet\next\scandigit}
+
+%D Watch the test for \type {\nextobeyedline}, because the
+%D endofline token can be \type {\def'd}, not \type {\let}'d,
+%D we need to do an indirect test (see \type {verb-ini.tex})
+%D for details. (This probably needs an update.)
+
+\ifx\normalmathshift\undefined \let\normalmathshift=$ \fi
+
+\unexpanded\def\scandigit
+ {\ifx\next\blankspace
+ \let\next\handledigits
+ \else\ifx\next\nextobeyedline % the indirect one
+ \let\next\handledigits
+ \else\ifx\next\bgroup
+ \let\next\handledigits
+ \else\ifx\next\egroup
+ \let\next\handledigits
+ \else\ifx\next\normalmathshift
+ \let\next\handledigits
+ \else
+ \let\next\collectdigit
+ \fi\fi\fi\fi\fi
+ \next}
+
+%D We store the power||of||ten (to be signaled by \type {^},
+%D \type {e} or~\type {E}) in a seperate macro so that we can
+%D typeset it in superscript. The space placeholders are
+%D replaced by a \type {@}.
+
+\unexpanded\def\savedigit#1#2%
+ {\edef#1{#1\saveddigits#2}\let\saveddigits\empty}
+
+\unexpanded\def\collectdigit#1%
+ {\ifx#1~%
+ \savedigit\collecteddigits @%
+ \else\if#1_% tricky as can be several catcodes ... will become lua code anyway
+ \savedigit\collecteddigits @%
+ \else\if\noexpand#1\relax
+ \let\grabdigit\handledigits
+ \else\ifcase\powerdigits
+ \if#1E%
+ \chardef\powerdigits\plusone
+ \else\if#1e%
+ \chardef\powerdigits\plusone
+ \else\if#1^%
+ \chardef\powerdigits\plusone
+ \else
+ \savedigit\collecteddigits#1%
+ %\doifelsenumber{#1}
+ % {\savedigit\collecteddigits#1}
+ % {\def\saveddigits{#1}}%
+ \fi\fi\fi
+ \else
+ \savedigit\savedpowerdigits#1%
+ %\doifelsenumber{#1}
+ % {\savedigit\savedpowerdigits#1}
+ % {\def\saveddigits{#1}}%
+ \fi\fi\fi\fi
+ \grabdigit}
+
+\let\handlemathdigits\firstofoneargument
+\let\handletextdigits\mathematics
+
+\unexpanded\def\handledigits
+ {%\ifcase\powerdigits
+ % \edef\collecteddigits{\collecteddigits\saveddigits}%
+ %\else
+ % \edef\savedpowerdigits{\savedpowerdigits\saveddigits}%
+ %\fi
+ \ifmmode
+ \handlemathdigits{\dohandledigits}%
+ \else
+ \dontleavehmode\hbox{\handletextdigits{\dohandledigits}}%
+ \fi
+ \egroup}
+
+%D Although we could do with one pass, a second pass for
+%D handling the stored sequence is more readable.
+
+\ifnum\texengine=\luatexengine
+
+ \def\dohandledigits
+ {\mathcode`\,="002C \mathcode`\.="002E % pretty hard coded
+ \expandafter\handletokens\collecteddigits\with\scandigits
+ \ifcase\powerdigits\else\digitpowerseparator^{\savedpowerdigits}\fi}
+
+ \chardef\mathaxisfontid\zerocount
+
+\else
+
+ \def\dohandledigits
+ {\mathcode`\,="013B \mathcode`\.="013A % pretty hard coded
+ \expandafter\handletokens\collecteddigits\with\scandigits
+ \ifcase\powerdigits\else\digitpowerseparator^{\savedpowerdigits}\fi}
+
+ \chardef\mathaxisfontid\plustwo
+
+\fi
+
+\def\doscandigit#1%
+ {\ifcase\skipdigit\@EA\hbox\else\@EA\hphantom\fi\bgroup
+ \mathematics % brr, needed because of stored punctuation
+ {\ifnum\digitinputmode=#1\relax
+ \ifcase\digitoutputmode
+ \or .%
+ \or ,%
+ \or \mskip\thinmuskip
+ \or \mskip\thinmuskip
+ \or \mskip\thickmuskip
+ \or \mskip\thickmuskip
+ \fi
+ \else
+ \ifodd\digitoutputmode,\else.\fi
+ \fi}%
+ \egroup}
+
+%D The signs can be made smaller and sqeezed into the width
+%D of a digit. Watch the \type {\mathaxisheight} trickery (this
+%D font related register stored the math axis).
+
+% 0,=
+% 0,== second = results in delta(00,=)
+% 0,- is invalid, should be =
+% 0,-- is invalid, should be ==
+
+\unexpanded\def\digitzeroamount
+ {\digitsgn\zeroamount
+ \def\digitzeroamount
+ {\hphantom
+ {00\setbox\scratchbox\hbox{$\zeroamount$}%
+ \hskip-\wd\scratchbox}%
+ \let\digitzeroamount\empty}}
+
+\unexpanded\def\scandigits#1%
+ {\if#1.\digitsep1\else
+ \if#1,\digitsep2\else
+ \if#1@\digitnop \else
+ \if#1_\digitnop \else
+ \if#1/\digitsgn{\hphantom{+}}\chardef\skipdigit0\else
+ \if#1-\ifcase\skipdigit\digitsgn-\else
+ \box\digitsepbox\digitzeroamount \fi\chardef\skipdigit0\else
+ \if#1+\digitsgn+\chardef\skipdigit0\else
+ \if#1=\box\digitsepbox\digitzeroamount \chardef\skipdigit0\else
+ \if#1s\digitsgn{\hphantom{\positive}}\chardef\skipdigit0\else
+ \if#1p\digitsgn\positive\chardef\skipdigit0\else
+ \if#1m\digitsgn\negative\chardef\skipdigit0\else
+ \if#1n\digitsgn\negative\chardef\skipdigit0\else
+ \box\digitsepbox #1\chardef\skipdigit0\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
+
+\newbox\digitsepbox \chardef\autodigitmode=1
+
+\unexpanded\def\digitsep#1%
+ {\ifcase\autodigitmode
+ \doscandigit#1%
+ \else
+ \setbox\digitsepbox\hbox{\doscandigit#1}%
+ \fi
+ \chardef\skipdigit0\relax}
+
+% strange, does not work
+%
+% \def\digitnop
+% {\hphantom{\box\digitsepbox}%
+% \hphantom{0}\chardef\skipdigit1\relax}
+%
+% while this works
+
+\unexpanded\def\digitnop
+ {\hbox{\hphantom{\box\digitsepbox}}%
+ \hphantom{0}\chardef\skipdigit1\relax}
+
+% but this doesn't
+%
+% \def\digitnop
+% {\hphantom{\box\digitsepbox0}%
+% \chardef\skipdigit1\relax}
+
+\unexpanded\def\digitsgn#1%
+ {\ifcase\digitsignmode#1\else
+ \hbox
+ {\setbox\scratchbox\hbox{0}%
+ \scratchdimen\mathaxisheight\textfont\mathaxisfontid
+ \def\digitsgn##1##2%
+ {\advance\scratchdimen-\mathaxisheight##1\mathaxisfontid
+ \raise\scratchdimen
+ \hbox to \wd\scratchbox{\hss$##2#1$\hss}}%
+ \ifcase\digitsignmode\or
+ \digitsgn\textfont \textstyle \or
+ \digitsgn\scriptfont \scriptstyle \or
+ \digitsgn\scriptscriptfont\scriptscriptstyle\fi}%
+ \fi}
+
+\ifx\undefined\zeroamount \def\zeroamount{-} \fi
+\ifx\undefined\positive \def\positive {+} \fi
+\ifx\undefined\negative \def\negative {-} \fi
+
+%D The digit parser handles a bunch of special characters as
+%D well as different formats. We strongly suggest you to use
+%D the grouped call.
+%D
+%D \starttabulate[|l|l|l|]
+%D \NC \type{.} \NC , . \NC comma or period \NC \NR
+%D \NC \type{,} \NC , . \NC comma or period \NC \NR
+%D \NC \type{@} \NC \NC invisible space \NC \NR
+%D \NC \type{_} \NC \NC invisible space \NC \NR
+%D \NC \type{/} \NC \NC invisible sign \NC \NR
+%D \NC \type{-} \NC $-$ \NC minus sign \NC \NR
+%D \NC \type{+} \NC $+$ \NC plus sign \NC \NR
+%D \NC \type{s} \NC \NC invisible high sign \NC \NR
+%D \NC \type{p} \NC $\positive$ \NC high plus sign \NC \NR
+%D \NC \type{m} \NC $\negative$ \NC high minus sign \NC \NR
+%D \NC \type{n} \NC $\negative$ \NC high minus (negative) sign \NC \NR
+%D \NC \type{=} \NC $\zeroamount$ \NC zero padding \NC \NR
+%D \stoptabulate
+%D
+%D These triggers are used in the following examples.
+%D
+%D \startbuffer
+%D \digits 12
+%D \digits{~~~.~~~.~~~.68.712,34}
+%D \digits ~~~.~~~.~~~.68.712,34
+%D \digits ___.___.111.68.712,34
+%D \digits 111.111.111.68.712,34
+%D \digits 12.345,90
+%D \digits 12.345.000
+%D \digits 12,34
+%D \digits{392.857.230.68.712,34}
+%D {\digits1234}
+%D \digits{1234}
+%D \digits 1234\relax
+%D $\digits 123.222,00$
+%D \digits 123.222,00
+%D \digits 123.222,==
+%D \digits 123.222,00^10
+%D \digits 123.222,00e10
+%D \digits /123.222,00e-12
+%D \digits -123.222,00e-12
+%D \digits +123.222,00e-12
+%D \digits n123.222,00e-12
+%D \digits s123.222,00e-12
+%D \digits p123.222,00e-12
+%D \stopbuffer
+%D
+%D \typebuffer
+%
+% \startlines
+% \getbuffer
+% \stoplines
+
+%D \macros
+%D {Digits}
+%D
+%D We also permit:
+
+\let\Digits\digits
+
+%D These macros are complicated by the fact that we also
+%D have to support cases like:
+%D
+%D \starttyping
+%D {\digits1234}
+%D \digits{1234}
+%D \digits 1234\whatever
+%D $\digits 123.222,00$
+%D \digits 123.222,00.
+%D \stoptyping
+%D
+%D The latter case shows us that trailing non digits are to
+%D be passed untreated.
+%D
+%D Another interesting case is:
+%D
+%D \starttyping
+%D \digits 123.222,00^10
+%D \stoptyping
+%D
+%D The separator is defined as:
+
+% \def\digitpowerseparator%
+% {\cdot10} % {\times10}
+
+\def\digitpowerseparator
+ {\ifx\collecteddigits\empty\else\cdot\fi10}
+
+%D \macros
+%D {digittemplate}
+%D
+%D Users can specify the way they enter those digits by saying
+%D something like:
+%D
+%D \starttyping
+%D \digittemplate 12.000.000,00 % \digittemplate .,
+%D \stoptyping
+
+\unexpanded\def\digittemplate #1 %
+ {\chardef\digitinputmode\zerocount
+ \handletokens#1\with\scandigittemplate}
+
+\unexpanded\def\scandigittemplate#1%
+ {\if #1.\ifcase\digitinputmode\chardef\digitinputmode\plusone \fi% period
+ \else\if#1,\ifcase\digitinputmode\chardef\digitinputmode\plustwo \fi% comma
+ \fi\fi}
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/m-pictex.mkiv b/tex/context/modules/mkiv/m-pictex.mkiv
new file mode 100644
index 000000000..73aad3f57
--- /dev/null
+++ b/tex/context/modules/mkiv/m-pictex.mkiv
@@ -0,0 +1,46 @@
+%D \module
+%D [ file=m-pictex,
+%D version=1997.01.15,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=\PICTEX\ Loading Macros,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Just in case someone still uses \PICTEX:
+
+\ifdefined\beginpicture \else
+
+ \ifdefined\grid \else
+ \let\normalgrid\grid
+ \fi
+
+ \ifdefined\axis \else
+ \let\normalaxis\axis
+ \fi
+
+ \ifdefined\fiverm \else
+ \font\fiverm=cmr5
+ \fi
+
+ \input prepictex.tex \relax
+ \input pictex.tex \relax
+ \input postpictex.tex \relax
+
+ \ifdefined\normalgrid
+ \let\pictexgrid\grid
+ \let\grid\normalgrid
+ \fi
+
+ \ifdefined\normalaxis
+ \let\pictexaxis\axis
+ \let\axis\normalaxis
+ \fi
+
+\fi
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/m-pipemode.mkiv b/tex/context/modules/mkiv/m-pipemode.mkiv
new file mode 100644
index 000000000..e96394c43
--- /dev/null
+++ b/tex/context/modules/mkiv/m-pipemode.mkiv
@@ -0,0 +1,7 @@
+% For Mojca: context --global m-pipemode.mkiv
+
+\disabledirectives[system.errorcontext]
+
+\starttext
+
+\let\stoptext\relax
diff --git a/tex/context/modules/mkiv/m-pstricks.lua b/tex/context/modules/mkiv/m-pstricks.lua
new file mode 100644
index 000000000..b151e313a
--- /dev/null
+++ b/tex/context/modules/mkiv/m-pstricks.lua
@@ -0,0 +1,74 @@
+if not modules then modules = { } end modules ['m-pstricks'] = {
+ version = 1.001,
+ comment = "companion to m-pstricks.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- The following will be done when I need ps tricks figures
+-- in large quantities:
+--
+-- + hash graphics and only process them once
+-- + save md5 checksums in tuc file
+--
+-- It's no big deal but has a low priority.
+
+local format, lower, concat, gmatch = string.format, string.lower, table.concat, string.gmatch
+local variables = interfaces.variables
+
+moduledata.pstricks = moduledata.pstricks or { }
+
+local report_pstricks = logs.reporter("pstricks")
+
+local template = [[
+\starttext
+ \pushcatcodetable
+ \setcatcodetable\texcatcodes
+ \usemodule[pstric]
+ %s
+ \popcatcodetable
+ \startTEXpage
+ \hbox\bgroup
+ \ignorespaces
+ %s
+ \removeunwantedspaces
+ \egroup
+ \obeydepth %% temp hack as we need to figure this out
+ \stopTEXpage
+\stoptext
+]]
+
+local loaded = { }
+local graphics = 0
+
+function moduledata.pstricks.usemodule(names)
+ for name in gmatch(names,"([^%s,]+)") do
+ loaded[#loaded+1] = format([[\readfile{%s}{}{}]],name)
+ end
+end
+
+function moduledata.pstricks.process(n)
+ graphics = graphics + 1
+ local name = format("%s-pstricks-%04i",tex.jobname,graphics)
+ local data = buffers.collectcontent("def-"..n)
+ local tmpfile = name .. ".tmp"
+ local epsfile = name .. ".ps"
+ local pdffile = name .. ".pdf"
+ local loaded = concat(loaded,"\n")
+ os.remove(epsfile)
+ os.remove(pdffile)
+ io.savedata(tmpfile,format(template,loaded,data))
+ os.execute(format("mtxrun --script texexec %s --once --dvips",tmpfile))
+ if lfs.isfile(epsfile) then
+ os.execute(format("ps2pdf %s %s",epsfile,pdffile))
+ -- todo: direct call but not now
+ if lfs.isfile(pdffile) then
+ context.externalfigure( { pdffile }, { object = variables.no } )
+ else
+ report_pstricks("run failed, no pdf file")
+ end
+ else
+ report_pstricks("run failed, no ps file")
+ end
+end
diff --git a/tex/context/modules/mkiv/m-pstricks.mkiv b/tex/context/modules/mkiv/m-pstricks.mkiv
new file mode 100644
index 000000000..421607aaf
--- /dev/null
+++ b/tex/context/modules/mkiv/m-pstricks.mkiv
@@ -0,0 +1,65 @@
+%D \module
+%D [ file=m-pstricks,
+%D version=2010.03.14,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=\PSTRICKS\ Connections,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{m-pstricks}{}
+
+\unprotect
+
+% best we can make a special colors module
+%
+% \let\@unused\plussixteen
+% \let\alloc@ \gobblefivearguments
+%
+% \def\loadpstrickscolors#1%
+% {\pushmacro\dodefinecolor
+% \pushmacro\dodefinepalet
+% \pushmacro\dodefinecolorgroup
+% \def\dodefinecolor[##1][##2]%
+% {\doifassignmentelse{##2}
+% {\getparameters[pstricks][r=0,g=0,b=0,##2]%
+% \expanded{\newrgbcolor{##1}{{\pstricksr} {\pstricksg} {\pstricksb}}}}%
+% {}}%
+% \def\dodefinepalet [##1][##2]{}%
+% \def\dodefinecolorgroup[##1][##2][##3]{}%
+% \writestatus{pstricks}{loading colors from #1}%
+% \input #1 \relax
+% \popmacro\dodefinecolorgroup
+% \popmacro\dodefinepalet
+% \popmacro\dodefinecolor}
+%
+% \input multido \relax
+% \input pstricks \relax
+% \input pst-plot \relax
+%
+% \loadpstrickscolors{colo-rgb}
+
+\definebuffer[PSTRICKS]
+
+\unexpanded\def\processPSTRICKS {\ctxlua{moduledata.pstricks.process(\thebuffernumber{PSTRICKS})}}
+\unexpanded\def\usePSTRICKSmodule[#1]{\ctxlua{moduledata.pstricks.usemodule("#1")}}
+\unexpanded\def\setPSTRICKS #1{\setbuffer[def-\thebuffernumber{PSTRICKS}]#1\endbuffer}
+
+\let\stopPSTRICKS\processPSTRICKS
+
+%D \starttyping
+%D \usemodule[pstricks]
+%D \usePSTRICKSmodule[pst-barcode]
+%D
+%D \startPSTRICKS
+%D \pspicture(-4mm,-1mm)(38mm,26mm)
+%D \psbarcode{9781860742712}{includetext guardwhitespace}{ean13}%
+%D \endpspicture
+%D \stopPSTRICKS
+%D \stoptyping
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/m-punk.mkiv b/tex/context/modules/mkiv/m-punk.mkiv
new file mode 100644
index 000000000..331e90d2e
--- /dev/null
+++ b/tex/context/modules/mkiv/m-punk.mkiv
@@ -0,0 +1,257 @@
+%D \module
+%D [ file=m-punk,
+%D version=2008.04.15,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Punk Support,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\ifx\luaversion\undefined \endinput \fi
+
+% At some point the font generation code will move into the
+% ConTeXt MkIV kernel.
+
+\startluacode
+local concat = table.concat
+local chardata = characters.data
+local fontdata = fonts.hashes.identifiers
+
+fonts.mp = fonts.mp or { }
+
+fonts.mp.version = fonts.mp.version or 1.15
+fonts.mp.inline = true
+fonts.mp.cache = containers.define("fonts", "mp", fonts.mp.version, true)
+
+metapost.characters = metapost.characters or { }
+
+-- todo: use table share as in otf
+
+local characters, descriptions = { }, { }
+local factor, l, n, w, h, d, total, variants = 100, { }, 0, 0, 0, 0, 0, 0, true
+
+-- A next version of mplib will provide the tfm font information which
+-- gives better glyph dimensions, plus additional kerning information.
+
+local flusher = {
+ startfigure = function(chrnum,llx,lly,urx,ury)
+ l, n = { }, chrnum
+ w, h, d = urx - llx, ury, -lly
+ total = total + 1
+ inline = fonts.mp.inline
+ end,
+ flushfigure = function(t)
+ for i=1, #t do
+ l[#l+1] = t[i]
+ end
+ end,
+ stopfigure = function()
+ local cd = chardata[n]
+ if inline then
+ descriptions[n] = {
+ -- unicode = n,
+ name = cd and cd.adobename,
+ width = w*100,
+ height = h*100,
+ depth = d*100,
+ boundingbox = { 0, -d, w, h },
+ }
+ characters[n] = {
+ commands = { -- todo: xforms, should happen in backend
+ { "special", "pdf: " .. concat(l," ") },
+ }
+ }
+ else
+ descriptions[n] = {
+ -- unicode = n,
+ name = cd and cd.adobename,
+ width = w*100,
+ height = h*100,
+ depth = d*100,
+ boundingbox = { 0, -d, w, h },
+ }
+ characters[n] = {
+ commands = {
+ { "image", { stream = concat(l," "), bbox = { 0, -d*65536, w*65536, h*65536 } } },
+ }
+ }
+ end
+ end
+}
+
+metapost.characters.instances = metapost.characters.instances or 10
+
+function metapost.characters.process(mpxformat, name, instances, scalefactor)
+ statistics.starttiming(metapost.characters)
+ scalefactor = scalefactor or 1
+ instances = instances or metapost.characters.instances or 10
+ local fontname = file.removesuffix(file.basename(name))
+ local hash = file.robustname(string.format("%s %05i %03i", fontname, scalefactor*1000, instances))
+ local lists = containers.read(fonts.mp.cache, hash)
+ if not lists then
+ statistics.starttiming(flusher)
+ -- we can use a format per font
+ local data = io.loaddata(resolvers.findfile(name))
+ metapost.reset(mpxformat)
+ metapost.setoutercolor(2) -- no outer color and no reset either
+ lists = { }
+ for i=1,instances do
+ characters = { }
+ descriptions = { }
+ metapost.process(
+ mpxformat,
+ {
+ "randomseed := " .. i*10 .. ";",
+ "scale_factor := " .. scalefactor .. " ;",
+ data
+ },
+ false,
+ flusher,
+ false,
+ false,
+ "all"
+ )
+ lists[i] = {
+ characters = characters,
+ descriptions = descriptions,
+ parameters = {
+ designsize = 655360,
+ slant = 0,
+ space = 333 * scalefactor,
+ space_stretch = 166.5 * scalefactor,
+ space_shrink = 111 * scalefactor,
+ x_height = 431 * scalefactor,
+ quad = 1000 * scalefactor,
+ extra_space = 0,
+ },
+ properties = {
+ name = string.format("%s-%03i",hash,i),
+ virtualized = true,
+ spacer = "space",
+ }
+ }
+ end
+ metapost.reset(mpxformat) -- saves memory
+ lists = containers.write(fonts.mp.cache, hash, lists)
+ statistics.stoptiming(flusher)
+ end
+ variants = variants + #lists
+ statistics.stoptiming(metapost.characters)
+ return lists
+end
+
+function fonts.handlers.vf.combiner.commands.metafont(g,v)
+ local size = g.specification.size
+ local data = metapost.characters.process(v[2],v[3],v[4],size/655360)
+ local list, t = { }, { }
+ for d=1,#data do
+ t = data[d]
+ t = fonts.constructors.scale(t, -1000)
+ local id = font.nextid()
+ t.fonts = { { id = id } }
+ fontdata[id] = t
+ fonts.handlers.vf.helpers.composecharacters(t)
+ list[d] = font.define(t)
+ end
+ for k, v in next, t do
+ g[k] = v -- kind of replace, when not present, make nil
+ end
+ g.properties.virtualized = true
+ g.variants = list
+end
+
+fonts.definers.methods.install( "punk", {
+ { "metafont", "mfplain", "punkfont.mp", 10 },
+} )
+fonts.definers.methods.install( "punkbold", {
+ { "metafont", "mfplain", "punkfont-bold.mp", 10 },
+} )
+fonts.definers.methods.install( "punkslanted", {
+ { "metafont", "mfplain", "punkfont-slanted.mp", 10 },
+} )
+fonts.definers.methods.install( "punkboldslanted", {
+ { "metafont", "mfplain", "punkfont-boldslanted.mp", 10 },
+} )
+
+-- typesetters.cases.register("RandomPunk", function(current)
+-- local used = fontdata[current].variants
+-- if used then
+-- local f = math.random(1,#used)
+-- current.font = used[f]
+-- return current, true
+-- else
+-- return current, false
+-- end
+-- end)
+
+local getfont = nodes.nuts.getfont
+local setfield = nodes.nuts.setfield
+local random = math.random
+
+typesetters.cases.register("RandomPunk", function(start)
+ local used = fontdata[getfont(start)].variants
+ if used then
+ local f = random(1,#used)
+ setfield(start,"font",used[f])
+ return start, true
+ else
+ return start, false
+ end
+end)
+
+metapost.characters.flusher = flusher
+
+statistics.register("metapost font generation", function()
+ local time = statistics.elapsedtime(flusher)
+ if total > 0 then
+ return string.format("%i glyphs, %.3f seconds runtime, %i glyphs/second", total, time, total/time)
+ else
+ return string.format("%i glyphs, %.3f seconds runtime", total, time)
+ end
+end)
+
+statistics.register("metapost font loading",function()
+ local time = statistics.elapsedtime(metapost.characters)
+ if variants > 0 then
+ return string.format("%.3f seconds, %i instances, %0.3f instances/second", time, variants, variants/time)
+ else
+ return string.format("%.3f seconds, %i instances", time, variants)
+ end
+end)
+\stopluacode
+
+\unexpanded\def\EnableRandomPunk {\setcharactercasing[RandomPunk]}
+\unexpanded\def\RandomPunk {\groupedcommand\EnableRandomPunk\donothing}
+\unexpanded\def\StartRandomPunk {\begingroup\EnableRandomPunk}
+\unexpanded\def\StopRandomPunk {\endgroup}
+
+\starttypescript [serif] [punk]
+ \definefontsynonym [Serif] [demo@punk]
+ \definefontsynonym [SerifBold] [demobold@punkbold]
+ \definefontsynonym [SerifSlanted] [demoslanted@punkslanted]
+ \definefontsynonym [SerifBoldSlanted] [demoboldslanted@punkboldslanted]
+ \definefontsynonym [SerifItalic] [SerifSlanted]
+ \definefontsynonym [SerifBoldItalic] [SerifBoldSlanted]
+\stoptypescript
+
+\starttypescript [punk]
+ \definetypeface [punk] [rm] [serif] [punk] [default]
+\stoptypescript
+
+\endinput
+
+\usetypescript[punk]
+
+\setupbodyfont[punk,14pt]
+
+\starttext
+ \definedfont[demo@punk at 10pt]hello world\par
+ \definedfont[demo@punk at 12pt]hello world\par
+ \definedfont[demo@punk at 16pt]hello world\par
+ \definedfont[demo@punk at 20pt]hello world\par
+\stoptext
+
diff --git a/tex/context/modules/mkiv/m-scite.mkiv b/tex/context/modules/mkiv/m-scite.mkiv
new file mode 100644
index 000000000..38c2f249e
--- /dev/null
+++ b/tex/context/modules/mkiv/m-scite.mkiv
@@ -0,0 +1,279 @@
+%D \module
+%D [ file=m-scite,
+%D version=2014.04.28,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=\SCITE\ lexers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% We can simplify the scite lexers, as long as we're able to return the
+% lexed result table and provide alexer module with the functions that
+% the lexer expects (so I need to decipher the cxx file).
+%
+% lexer._TOKENSTYLES : table
+% lexer._CHILDREN : flag
+% lexer._EXTRASTYLES : table
+% lexer._GRAMMAR : flag
+%
+% lexers.load : function
+% lexers.lex : function
+%
+% And some properties that map styles onto scintilla styling. I get the
+% impression that we end up with something simpler, a hybrid between the
+% scite lexing and the current context way, so we get an intermediate
+% step, with some penalty for context, but at least I don't have to
+% maintain two sets (three sets as we also have a line based series).
+
+% TODO: as these files are in tds we can locate them and set the lexer root
+% to that one. Currently we're on: we're on context/documents.
+
+% TODO: tab
+
+% This is an experiment: eventually we need to hook it into the verbatim code
+% and deal with widow lines and so.
+
+\startluacode
+
+-- todo: merge with collapse
+-- todo: prehash whitespaces
+
+-- todo: hook into the pretty print code
+-- todo: a simple catcode regime with only \ { }
+
+local gsub, sub, find = string.gsub, string.sub, string.find
+local concat = table.concat
+local formatters = string.formatters
+local lpegmatch = lpeg.match
+local setmetatableindex = table.setmetatableindex
+
+local scite = require("util-sci")
+buffers.scite = scite
+
+-- context output:
+
+local f_def_color = formatters["\\definecolor[slxc%s][h=%s%s%s]%%"]
+local f_fore_none = formatters["\\def\\slx%s#1{{\\slxc%s#1}}%%"]
+local f_fore_bold = formatters["\\def\\slx%s#1{{\\slxc%s\\bf#1}}%%"]
+local f_none_bold = formatters["\\def\\slx%s#1{{\\bf#1}}%%"]
+local f_none_none = formatters["\\def\\slx%s#1{{#1}}%%"]
+local f_texstyled = formatters["\\slx%s{%s}"]
+
+local f_mapping = [[
+\let\string\slxL\string\letterleftbrace
+\let\string\slxR\string\letterrightbrace
+\let\string\slxM\string\letterdollar
+\let\string\slxV\string\letterbar
+\let\string\slxU\string\letterhat
+\let\string\slxD\string\letterunderscore
+\let\string\slxH\string\letterhash
+\let\string\slxB\string\letterbackslash
+\let\string\slxP\string\letterpercent
+\let\string\slxT\string\lettertilde
+\let\string\slxS\string\fixedspace
+%]]
+
+local replacer = lpeg.replacer {
+ ["{"] = "\\slxL ",
+ ["}"] = "\\slxR ",
+ ["$"] = "\\slxM ",
+ ["^"] = "\\slxU ",
+ ["_"] = "\\slxD ",
+ ["|"] = "\\slxV ",
+ ["#"] = "\\slxH ",
+ ["\\"] = "\\slxB ",
+ ["%"] = "\\slxP ",
+ ["~"] = "\\slxT ",
+ [" "] = "\\slxS ",
+}
+
+local colors = nil
+
+local function exportcolors()
+ if not colors then
+ scite.loadscitelexer()
+ local function black(f)
+ return (f[1] == f[2]) and (f[2] == f[3]) and (f[3] == '00')
+ end
+ local result, r = { f_mapping }, 1
+ for k, v in table.sortedhash(lexer.context.styles) do
+ local fore = v.fore
+ if fore and not black(fore) then
+ r = r + 1
+ result[r] = f_def_color(k,fore[1],fore[2],fore[3])
+ end
+ end
+ r = r + 1
+ result[r] = "%"
+ for k, v in table.sortedhash(lexer.context.styles) do
+ local bold = v.bold
+ local fore = v.fore
+ r = r + 1
+ if fore and not black(fore) then
+ if bold then
+ result[r] = f_fore_bold(k,k)
+ else
+ result[r] = f_fore_none(k,k)
+ end
+ else
+ if bold then
+ result[r] = f_none_bold(k)
+ else
+ result[r] = f_none_none(k)
+ end
+ end
+ end
+ colors = concat(result,"\n")
+ end
+ return colors
+end
+
+local function exportwhites()
+ return setmetatableindex(function(t,k)
+ local v = find(k,"white") and true or false
+ t[k] = v
+ return v
+ end)
+end
+
+local function exportstyled(lexer,text)
+ local result = lexer.lex(lexer,text,0)
+ local start = 1
+ local whites = exportwhites()
+ local buffer = { }
+ for i=1,#result,2 do
+ local style = result[i]
+ local position = result[i+1]
+ local txt = sub(text,start,position-1)
+ txt = lpegmatch(replacer,txt)
+ if whites[style] then
+ buffer[#buffer+1] = txt
+ else
+ buffer[#buffer+1] = f_texstyled(style,txt)
+ end
+ start = position
+ end
+ buffer = concat(buffer)
+ return buffer
+end
+
+function scite.installcommands()
+ context(exportcolors())
+end
+
+local function lexdata(data,lexname)
+ buffers.assign("lex",exportstyled(scite.loadedlexers[lexname],data or ""))
+end
+
+scite.lexdata = lexdata
+
+function scite.lexbuffer(name,lexname)
+ lexdata(buffers.getcontent(name) or "",lexname or "tex")
+end
+
+function scite.lexfile(filename,lexname)
+ lexdata(io.loaddata(filename) or "",lexname or file.suffix(filename))
+end
+
+-- html output
+
+\stopluacode
+
+% This is a preliminary interface.
+
+\unprotect
+
+\unexpanded\def\installscitecommands
+ {\ctxlua{buffers.scite.installcommands()}%
+ \let\installscitecommands\relax}
+
+\unexpanded\def\startscite{\startlines}
+\unexpanded\def\stopscite {\stoplines}
+
+\unexpanded\def\scitefile
+ {\dosingleargument\module_scite_file}
+
+\unexpanded\def\module_scite_file[#1]%
+ {\start
+ \ctxlua{buffers.scite.lexfile("#1")}%
+ \installscitecommands
+ \tt
+ \dontcomplain
+ \setcatcodetable\ctxcatcodes % needed in xml
+ \startscite
+ \getbuffer[lex]%
+ \stopscite
+ \stop}
+
+\unexpanded\def\scitebuffer
+ {\dodoubleargument\module_scite_buffer}
+
+\unexpanded\def\module_scite_buffer[#1][#2]%
+ {\start
+ \ifsecondargument
+ \ctxlua{buffers.scite.lexbuffer("#2","#1")}%
+ \else
+ \ctxlua{buffers.scite.lexbuffer("#1","tex")}%
+ \fi
+ \installscitecommands
+ \tt
+ \dontcomplain
+ \setcatcodetable\ctxcatcodes % needed in xml
+ \startscite
+ \getbuffer[lex]%
+ \stopscite
+ \stop}
+
+\protect
+
+\continueifinputfile{m-scite.mkiv}
+
+\setupbodyfont[dejavu,8pt]
+
+\setuplayout
+ [width=middle,
+ height=middle,
+ header=1cm,
+ footer=1cm,
+ topspace=1cm,
+ bottomspace=1cm,
+ backspace=1cm]
+
+\startbuffer[demo]
+\startsubsubject[title={oeps}]
+
+\startMPcode
+ draw fullcircle
+ scaled 2cm
+ withpen pencircle scaled 1mm
+ withcolor .5green;
+ draw textext (
+ lua (
+ "local function f(s) return string.upper(s) end mp.quoted(f('foo'))"
+ )
+ ) withcolor .5red ;
+\stopMPcode
+
+\startluacode
+ context("foo")
+\stopluacode
+
+\stopsubsubject
+\stopbuffer
+
+\starttext
+
+% \scitefile[../lexers/scite-context-lexer.lua] \page
+% \scitefile[t:/manuals/about/about-metafun.tex] \page
+% \scitefile[t:/sources/strc-sec.mkiv] \page
+% \scitefile[e:/tmp/mp.w] \page
+% \scitefile[t:/manuals/hybrid/tugboat.bib] \page
+\scitefile[e:/tmp/test.bib] \page
+
+% \getbuffer[demo] \scitebuffer[demo]
+
+\stoptext
diff --git a/tex/context/modules/mkiv/m-spreadsheet.lua b/tex/context/modules/mkiv/m-spreadsheet.lua
new file mode 100644
index 000000000..1b3c5cb34
--- /dev/null
+++ b/tex/context/modules/mkiv/m-spreadsheet.lua
@@ -0,0 +1,332 @@
+if not modules then modules = { } end modules ['m-spreadsheet'] = {
+ version = 1.001,
+ comment = "companion to m-spreadsheet.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local byte, format, gsub, find = string.byte, string.format, string.gsub, string.find
+local R, P, S, C, V, Cs, Cc, Ct, Cg, Cf, Carg = lpeg.R, lpeg.P, lpeg.S, lpeg.C, lpeg.V, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.Cg, lpeg.Cf, lpeg.Carg
+local lpegmatch, patterns = lpeg.match, lpeg.patterns
+local setmetatable, loadstring, next, tostring, tonumber,rawget = setmetatable, loadstring, next, tostring, tonumber, rawget
+local formatters = string.formatters
+
+local context = context
+
+local splitthousands = utilities.parsers.splitthousands
+local variables = interfaces.variables
+
+local v_yes = variables.yes
+
+moduledata = moduledata or { }
+
+local spreadsheets = { }
+moduledata.spreadsheets = spreadsheets
+
+local data = {
+ -- nothing yet
+}
+
+local settings = {
+ period = ".",
+ comma = ",",
+}
+
+spreadsheets.data = data
+spreadsheets.settings = settings
+
+local defaultname = "default"
+local stack = { }
+local current = defaultname
+
+local d_mt ; d_mt = {
+ __index = function(t,k)
+ local v = { }
+ setmetatable(v,d_mt)
+ t[k] = v
+ return v
+ end,
+}
+
+local s_mt ; s_mt = {
+ __index = function(t,k)
+ local v = settings[k]
+ t[k] = v
+ return v
+ end,
+}
+
+function spreadsheets.setup(t)
+ for k, v in next, t do
+ settings[k] = v
+ end
+end
+
+local function emptydata(name,settings)
+ local data = { }
+ local specifications = { }
+ local settings = settings or { }
+ setmetatable(data,d_mt)
+ setmetatable(specifications,d_mt)
+ setmetatable(settings,s_mt)
+ return {
+ name = name,
+ data = data,
+ maxcol = 0,
+ maxrow = 0,
+ settings = settings,
+ temp = { }, -- for local usage
+ specifications = specifications,
+ }
+end
+
+function spreadsheets.reset(name)
+ if not name or name == "" then name = defaultname end
+ data[name] = emptydata(name,data[name] and data[name].settings)
+end
+
+function spreadsheets.start(name,s)
+ if not name or name == "" then
+ name = defaultname
+ end
+ if not s then
+ s = { }
+ end
+ table.insert(stack,current)
+ current = name
+ if data[current] then
+ setmetatable(s,s_mt)
+ data[current].settings = s
+ else
+ data[current] = emptydata(name,s)
+ end
+end
+
+function spreadsheets.stop()
+ current = table.remove(stack)
+end
+
+spreadsheets.reset()
+
+local offset = byte("A") - 1
+
+local function assign(s,n)
+ return formatters["moduledata.spreadsheets.data['%s'].data[%s]"](n,byte(s)-offset)
+end
+
+function datacell(a,b,...)
+ local n = 0
+ if b then
+ local t = { a, b, ... }
+ for i=1,#t do
+ n = n * (i-1) * 26 + byte(t[i]) - offset
+ end
+ else
+ n = byte(a) - offset
+ end
+ return formatters["dat[%s]"](n)
+end
+
+local function checktemplate(s)
+ if find(s,"%",1,true) then
+ -- normal template
+ return s
+ elseif find(s,"@",1,true) then
+ -- tex specific template
+ return gsub(s,"@","%%")
+ else
+ -- tex specific quick template
+ return "%" .. s
+ end
+end
+
+local quoted = Cs(patterns.unquoted)
+local spaces = patterns.whitespace^0
+local cell = C(R("AZ"))^1 / datacell * (Cc("[") * (R("09")^1) * Cc("]") + #P(1))
+
+-- A nasty aspect of lpeg: Cf ( spaces * Cc("") * { "start" ... this will create a table that will
+-- be reused, so we accumulate!
+
+local pattern = Cf ( spaces * Ct("") * { "start",
+ start = V("value") + V("set") + V("format") + V("string") + V("code"),
+ value = Cg(P([[=]]) * spaces * Cc("kind") * Cc("value")) * V("code"),
+ set = Cg(P([[!]]) * spaces * Cc("kind") * Cc("set")) * V("code"),
+ format = Cg(P([[@]]) * spaces * Cc("kind") * Cc("format")) * spaces * Cg(Cc("template") * Cs(quoted/checktemplate)) * V("code"),
+ string = Cg(#S([["']]) * Cc("kind") * Cc("string")) * Cg(Cc("content") * quoted),
+ code = spaces * Cg(Cc("code") * Cs((cell + P(1))^0)),
+}, rawset)
+
+local functions = { }
+spreadsheets.functions = functions
+
+function functions._s_(row,col,c,f,t)
+ local r = 0
+ if f and t then -- f..t
+ -- ok
+ elseif f then -- 1..f
+ f, t = 1, f
+ else
+ f, t = 1, row - 1
+ end
+ for i=f,t do
+ local ci = c[i]
+ if type(ci) == "number" then
+ r = r + ci
+ end
+ end
+ return r
+end
+
+functions.fmt = string.tformat
+
+local f_code = formatters [ [[
+ local _m_ = moduledata.spreadsheets
+ local dat = _m_.data['%s'].data
+ local tmp = _m_.temp
+ local fnc = _m_.functions
+ local row = %s
+ local col = %s
+ function fnc.sum(...) return fnc._s_(row,col,...) end
+ local sum = fnc.sum
+ local fmt = fnc.fmt
+ return %s
+]] ]
+
+-- to be considered: a weak cache
+
+local function propername(name)
+ if name ~= "" then
+ return name
+ elseif current ~= "" then
+ return current
+ else
+ return defaultname
+ end
+end
+
+-- if name == "" then name = current if name == "" then name = defaultname end end
+
+local function execute(name,r,c,str)
+ if str ~= "" then
+ local d = data[name]
+ if c > d.maxcol then
+ d.maxcol = c
+ end
+ if r > d.maxrow then
+ d.maxrow = r
+ end
+ local specification = lpegmatch(pattern,str,1,name)
+ d.specifications[c][r] = specification
+ local kind = specification.kind
+ if kind == "string" then
+ return specification.content or ""
+ else
+ local code = specification.code
+ if code and code ~= "" then
+ code = f_code(name,r,c,code or "")
+ local result = loadstring(code) -- utilities.lua.strippedloadstring(code,true) -- when tracing
+ result = result and result()
+ if type(result) == "function" then
+ result = result()
+ end
+ if type(result) == "number" then
+ d.data[c][r] = result
+ end
+ if not result then
+ -- nothing
+ elseif kind == "set" then
+ -- no return
+ elseif kind == "format" then
+ return formatters[specification.template](result)
+ else
+ return result
+ end
+ end
+ end
+ end
+end
+
+function spreadsheets.set(name,r,c,str)
+ name = propername(name)
+ execute(name,r,c,str)
+end
+
+function spreadsheets.get(name,r,c,str)
+ name = propername(name)
+ local dname = data[name]
+ if not dname then
+ -- nothing
+ elseif not str or str == "" then
+ context(dname.data[c][r] or 0)
+ else
+ local result = execute(name,r,c,str)
+ if result then
+-- if type(result) == "number" then
+-- dname.data[c][r] = result
+-- result = tostring(result)
+-- end
+ local settings = dname.settings
+ local split = settings.split
+ local period = settings.period
+ local comma = settings.comma
+ if split == v_yes then
+ result = splitthousands(result)
+ end
+ if period == "" then period = nil end
+ if comma == "" then comma = nil end
+ result = gsub(result,".",{ ["."] = period, [","] = comma })
+ context(result)
+ end
+ end
+end
+
+function spreadsheets.doifelsecell(name,r,c)
+ name = propername(name)
+ local d = data[name]
+ local d = d and d.data
+ local r = d and rawget(d,r)
+ local c = r and rawget(r,c)
+ commands.doifelse(c)
+end
+
+local function simplify(name)
+ name = propername(name)
+ local data = data[name]
+ if data then
+ data = data.data
+ local temp = { }
+ for k, v in next, data do
+ local t = { }
+ temp[k] = t
+ for kk, vv in next, v do
+ if type(vv) == "function" then
+ t[kk] = "<function>"
+ else
+ t[kk] = vv
+ end
+ end
+ end
+ return temp
+ end
+end
+
+local function serialize(name)
+ local s = simplify(name)
+ if s then
+ return table.serialize(s,name)
+ else
+ return formatters["<unknown spreadsheet %a>"](name)
+ end
+end
+
+spreadsheets.simplify = simplify
+spreadsheets.serialize = serialize
+
+function spreadsheets.inspect(name)
+ inspect(serialize(name))
+end
+
+function spreadsheets.tocontext(name)
+ context.tocontext(simplify(name))
+end
diff --git a/tex/context/modules/mkiv/m-spreadsheet.mkiv b/tex/context/modules/mkiv/m-spreadsheet.mkiv
new file mode 100644
index 000000000..914a2b57a
--- /dev/null
+++ b/tex/context/modules/mkiv/m-spreadsheet.mkiv
@@ -0,0 +1,221 @@
+%D \module
+%D [ file=m-spreadsheet,
+%D version=2011.02.21,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=Spreadsheets,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is an experimental follow up on discussion on the mailing list.
+
+\registerctxluafile{m-spreadsheet}{1.001}
+
+\unprotect
+
+% todo: get(...) set(..) ctx(...)
+
+\installcorenamespace{spreadsheet}
+
+\installcommandhandler \??spreadsheet {spreadsheet} \??spreadsheet
+
+\appendtoks
+ \ctxlua{moduledata.spreadsheets.setup{ % global !
+ period = \!!bs\spreadsheetparameter\c!period\!!es,
+ comma = \!!bs\spreadsheetparameter\c!comma\!!es,
+ split = \!!bs\spreadsheetparameter\c!split\!!es,
+ }}%
+\to \everysetupspreadsheet
+
+\setupspreadsheet
+ [%\c!comma=,
+ %\c!period=,
+ \c!split=\v!no]
+
+\unexpanded\def\resetspreadsheet
+ {\dosingleempty\module_spreadsheet_reset}
+
+\unexpanded\def\module_spreadsheet_reset[#1]%
+ {\ctxlua{moduledata.spreadsheets.reset("#1")}}
+
+\unexpanded\def\startspreadsheet
+ {\dosingleempty\module_spreadsheet_start}
+
+\unexpanded\def\module_spreadsheet_start[#1]%
+ {\pushmacro\currentspreadsheet
+ \edef\currentspreadsheet{#1}%
+ \checkspreadsheetparent
+ \edef\m_spreadsheet_period{\spreadsheetparameter\c!period}%
+ \edef\m_spreadsheet_comma {\spreadsheetparameter\c!comma}%
+ \ctxlua{moduledata.spreadsheets.start("#1", {
+ period = \!!bs\detokenize\expandafter{\m_spreadsheet_period}\!!es,
+ comma = \!!bs\detokenize\expandafter{\m_spreadsheet_comma}\!!es,
+ split = \!!bs\spreadsheetparameter\c!split\!!es,
+ })}}
+
+\unexpanded\def\stopspreadsheet
+ {\ctxlua{moduledata.spreadsheets.stop()}%
+ \popmacro\currentspreadsheet}
+
+\unexpanded\def\showspreadsheet
+ {\dosingleempty\module_spreadsheet_show}
+
+\unexpanded\def\module_spreadsheet_show[#1]%
+ {\ctxlua{moduledata.spreadsheets.tocontext("#1")}}
+
+\unexpanded\def\inspectspreadsheet
+ {\dosingleempty\module_spreadsheet_inspect}
+
+\unexpanded\def\module_spreadsheet_inspect[#1]%
+ {\ctxlua{moduledata.spreadsheets.inspect("#1")}}
+
+\unexpanded\def\setspreadsheet
+ {\dosingleempty\module_spreadsheet_set}
+
+\unexpanded\def\module_spreadsheet_set[#1]#2#3#4%
+ {\ctxlua{moduledata.spreadsheets.set("#1",\number#2,\number#3,"#4")}}
+
+\unexpanded\def\getspreadsheet
+ {\dosingleempty\module_spreadsheet_get}
+
+\unexpanded\def\module_spreadsheet_get[#1]#2#3#4%
+ {\ctxlua{moduledata.spreadsheets.get("#1",\number#2,\number#3,"#4")}}
+
+\unexpanded\def\doifelsespreadsheetcell
+ {\dosingleempty\module_spreadsheet_doifelse_cell}
+
+\let\doifspreadsheetcellelse\doifelsespreadsheetcell
+
+\unexpanded\def\module_spreadsheet_doifelse_cell[#1]#2#3%
+ {\ctxlua{moduledata.spreadsheets.doifelsecell("#1",\number#2,\number#3)}}
+
+\ifdefined\tblrow
+
+ \def\TABLEsetspreadsheet#1{\ctxlua{moduledata.spreadsheets.set("",\number\tblrow+1,\number\tblcol,\!!bs#1\!!es)}}
+ \def\TABLEgetspreadsheet#1{\ctxlua{moduledata.spreadsheets.get("",\number\tblrow+1,\number\tblcol,\!!bs#1\!!es)}}
+
+\else
+
+ \def\TABLEsetspreadsheet#1{\ctxlua{moduledata.spreadsheets.set("",\number\c_tabl_ntb_row+1,\number\c_tabl_ntb_col,\!!bs#1\!!es)}}
+ \def\TABLEgetspreadsheet#1{\ctxlua{moduledata.spreadsheets.get("",\number\c_tabl_ntb_row+1,\number\c_tabl_ntb_col,\!!bs#1\!!es)}}
+
+\fi
+
+\appendtoks
+ \module_spreadsheet_reset[\currentspreadsheet]%
+ \let\setspr\TABLEsetspreadsheet
+ \let\getspr\TABLEgetspreadsheet
+\to \everyTABLEpass
+
+\unexpanded\def\startspreadsheettable % quick and dirty
+ {\dodoubleempty\module_spreadsheet_start_table}
+
+\unexpanded\def\module_spreadsheet_start_table[#1][#2]%
+ {\bgroup
+ \let\startrow \module_spreadsheet_row_start
+ \let\stoprow \module_spreadsheet_row_stop
+ \let\startcell\module_spreadsheet_cell_start
+ \let\stopcell \module_spreadsheet_cell_stop
+ \doifelseassignment{#1}
+ {\module_spreadsheet_start
+ \directsetup{spreadsheet:before:\currentspreadsheet}%
+ \bTABLE[\c!align=\v!flushright,#1]}
+ {\module_spreadsheet_start[#1]%
+ \directsetup{spreadsheet:before:\currentspreadsheet}%
+ \bTABLE[\c!align=\v!flushright,#2]}}
+
+\unexpanded\def\stopspreadsheettable
+ {\eTABLE
+ \directsetup{spreadsheet:after:\currentspreadsheet}%
+ \stopspreadsheet
+ \egroup}
+
+\unexpanded\def\module_spreadsheet_row_start{\bTR}
+\unexpanded\def\module_spreadsheet_row_stop {\eTR}
+
+\unexpanded\def\module_spreadsheet_cell_start
+ {\doifelsenextoptional\module_spreadsheet_cell_start_yes\module_spreadsheet_cell_start_nop}
+
+\unexpanded\def\module_spreadsheet_cell_start_yes[#1]#2\stopcell
+ {\bTD[#1]\getspr{#2}\eTD}
+
+\unexpanded\def\module_spreadsheet_cell_start_nop#1\stopcell
+ {\bTD\getspr{#1}\eTD}
+
+\let\module_spreadsheet_cell_stop\relax
+
+\protect
+
+\continueifinputfile{m-spreadsheet.mkiv}
+
+\starttext
+
+\bTABLE[align=middle]
+ \bTR
+ \bTD \getspr{100} \eTD \bTD test \setspr{30} \eTD
+ \eTR
+ \bTR
+ \bTD \getspr{20} \eTD \bTD \getspr{4+3} \eTD
+ \eTR
+ \bTR
+ \bTD \getspr{A[1] + A[2]} \eTD
+ \bTD \getspr{B1 + B2} \eTD
+ \eTR
+ \bTR
+ \bTD[nx=2] \bf \getspr{(A[3] + B[3]) /100} \eTD
+ \eTR
+ \bTR
+ \bTD[nx=2] \bf \getspr{string.format("\letterpercent0.3f",(A[3] + B[3]) /100)} \eTD
+ \eTR
+ \bTR
+ \bTD[nx=2] \bf \getspr{fmt("@0.3f",(sum(A,1,2)) / 10)} \eTD
+ \eTR
+\eTABLE
+
+\setupspreadsheet[mysheet]
+
+\startspreadsheet[mysheet]
+
+\bTABLE[align=middle]
+ \bTR
+ \bTD \getspr{100} \eTD \bTD test \setspr{30} \eTD
+ \eTR
+ \bTR
+ \bTD \getspr{20} \eTD \bTD \getspr{4+3.5} \eTD
+ \eTR
+ \bTR
+ \bTD \getspr{A[1] + A[2]} \eTD
+ \bTD \getspr{B[1] + B[2]} \eTD
+ \eTR
+ \bTR
+ \bTD[nx=2] \bf \getspr{A[3] + B[3]} \eTD
+ \eTR
+\eTABLE
+
+\stopspreadsheet
+
+\blank
+
+\setupspreadsheet[test][period={{\bf\middlered .}},comma={{\bf\middlegreen ,}},split=yes]
+
+\startspreadsheettable[test]
+ \startrow
+ \startcell 123456.78 \stopcell
+ \startcell 1234567.89 \stopcell
+ \startcell A[1] + B[1] \stopcell
+ \stoprow
+\stopspreadsheettable
+
+\blank
+
+% \showspreadsheet
+% \showspreadsheet[mysheet]
+
+\doifelsespreadsheetcell[mysheet]{1}{2}{YES}{NOP}
+\doifelsespreadsheetcell[myshoot]{1}{2}{YES}{NOP}
+
+\stoptext
diff --git a/tex/context/modules/mkiv/m-sql.mkiv b/tex/context/modules/mkiv/m-sql.mkiv
new file mode 100644
index 000000000..f1df2833a
--- /dev/null
+++ b/tex/context/modules/mkiv/m-sql.mkiv
@@ -0,0 +1,29 @@
+%D \module
+%D [ file=m-sql,
+%D version=2012.07.12,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=SQL,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[sql]
+
+\registerctxluafile{util-tpl}{}
+\registerctxluafile{util-sql}{}
+
+\setupmodule
+
+\doifsomething {\currentmoduleparameter{method}}{
+ \ctxlua{utilities.sql.setmethod("\currentmoduleparameter{method}")}
+}
+
+\doifsomething {\currentmoduleparameter{server}} {
+ \ctxlua{utilities.sql.setserver("\currentmoduleparameter{server}")}
+}
+
+\stopmodule
diff --git a/tex/context/modules/mkiv/m-steps.lua b/tex/context/modules/mkiv/m-steps.lua
new file mode 100644
index 000000000..8eb481550
--- /dev/null
+++ b/tex/context/modules/mkiv/m-steps.lua
@@ -0,0 +1,226 @@
+if not modules then modules = { } end modules ['x-flow'] = {
+ version = 1.001,
+ comment = "companion to m-flow.mkvi",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- when we can resolve mpcolor at the lua end we will use metapost.graphic(....) directly
+
+moduledata.steps = moduledata.steps or { }
+
+local variables = interfaces.variables
+
+local trace_charts = false
+
+local defaults = {
+ chart = {
+ dx = 10*65436,
+ dy = 10*65436,
+ },
+ cell = {
+ alternative = 1,
+ offset = 2*65436,
+ rulethickness = 65436,
+ framecolor = "blue",
+ backgroundcolor = "gray",
+ },
+ text = {
+ alternative = 1,
+ offset = 2*65436,
+ distance = 4*65436,
+ rulethickness = 65436,
+ framecolor = "red",
+ backgroundcolor = "gray",
+ },
+ line = {
+ alternative = 1,
+ rulethickness = 65436,
+ height = 30*65436,
+ distance = 10*65436,
+ offset = 5*65436,
+ color = "green",
+ },
+}
+
+-- todo : name (no name then direct)
+-- maybe: includes
+-- maybe: flush ranges
+
+local charts = { }
+local steps = { }
+
+function commands.step_start_chart(name)
+ name = name or ""
+ steps = { }
+ charts[name] = {
+ steps = steps,
+ }
+end
+
+function commands.step_stop_chart()
+end
+
+function commands.step_make_chart(settings)
+ local chartsettings = settings.chart
+ if not chartsettings then
+ print("no chart")
+ return
+ end
+ local chartname = chartsettings.name
+ if not chartname then
+ print("no name given")
+ return
+ end
+ local chart = charts[chartname]
+ if not chart then
+ print("no such chart",chartname)
+ return
+ end
+ local steps = chart.steps or { }
+ --
+ table.setmetatableindex(settings,defaults)
+ --
+ if trace_charts then
+ inspect(steps)
+ end
+ --
+ local textsettings = settings.text
+ local cellsettings = settings.cell
+ local linesettings = settings.line
+ --
+ context.startMPcode()
+ context("if unknown context_cell : input mp-step.mpiv ; fi ;")
+ context("step_begin_chart ;")
+ --
+ if chartsettings.alternative == variables.vertical then
+ context("chart_vertical := true ;")
+ end
+ --
+ context("text_line_color := \\MPcolor{%s} ;", textsettings.framecolor)
+ context("text_line_width := %p ;", textsettings.rulethickness)
+ context("text_fill_color := \\MPcolor{%s} ;", textsettings.backgroundcolor)
+ context("text_offset := %p ;", textsettings.offset)
+ context("text_distance_set := %p ;", textsettings.distance)
+ --
+ context("cell_line_color := \\MPcolor{%s} ;", cellsettings.framecolor)
+ context("cell_line_width := %p ;", cellsettings.rulethickness)
+ context("cell_fill_color := \\MPcolor{%s} ;", cellsettings.backgroundcolor)
+ context("cell_offset := %p ;", cellsettings.offset)
+ context("cell_distance_x := %p ;", cellsettings.dx)
+ context("cell_distance_y := %p ;", cellsettings.dy)
+ --
+ context("line_line_color := \\MPcolor{%s} ;", linesettings.color)
+ context("line_line_width := %p ;", linesettings.rulethickness)
+ context("line_distance := %p ;", linesettings.distance)
+ context("line_offset := %p ;", linesettings.offset)
+ --
+ for i=1,#steps do
+ local step = steps[i]
+ context("step_begin_cell ;")
+ if step.cell_top ~= "" then
+ context('step_cell_top("%s") ;',string.strip(step.cell_top))
+ end
+ if step.cell_bot ~= "" then
+ context('step_cell_bot("%s") ;',string.strip(step.cell_bot))
+ end
+ if step.text_top ~= "" then
+ context('step_text_top("%s") ;',string.strip(step.text_top))
+ end
+ if step.text_mid ~= "" then
+ context('step_text_mid("%s") ;',string.strip(step.text_mid))
+ end
+ if step.text_bot ~= "" then
+ context('step_text_bot("%s") ;',string.strip(step.text_bot))
+ end
+ context("step_end_cell ;")
+ end
+ --
+ context("step_end_chart ;")
+ context.stopMPcode()
+end
+
+function commands.step_cells(top,bot)
+ steps[#steps+1] = {
+ cell_top = top or "",
+ cell_bot = bot or "",
+ text_top = "",
+ text_mid = "",
+ text_bot = "",
+ }
+end
+
+function commands.step_texts(top,bot)
+ if #steps > 0 then
+ steps[#steps].text_top = top or ""
+ steps[#steps].text_bot = bot or ""
+ end
+end
+
+function commands.step_cell(top)
+ steps[#steps+1] = {
+ cell_top = top or "",
+ cell_bot = "",
+ text_top = "",
+ text_mid = "",
+ text_bot = "",
+ }
+end
+
+function commands.step_text(top)
+ if #steps > 0 then
+ steps[#steps].text_top = top or ""
+ end
+end
+
+function commands.step_textset(left,middle,right)
+ if #steps > 0 then
+ steps[#steps].text_top = left or ""
+ steps[#steps].text_mid = middle or ""
+ steps[#steps].text_bot = right or ""
+ end
+end
+
+function commands.step_start_cell()
+ steps[#steps+1] = {
+ cell_top = "",
+ cell_bot = "",
+ text_top = "",
+ text_mid = "",
+ text_bot = "",
+ }
+end
+
+function commands.step_stop_cell()
+end
+
+function commands.step_text_top(str)
+ if #steps > 0 then
+ steps[#steps].text_top = str or ""
+ end
+end
+
+function commands.step_text_mid(str)
+ if #steps > 0 then
+ steps[#steps].text_mid = str or ""
+ end
+end
+
+function commands.step_text_bot(str)
+ if #steps > 0 then
+ steps[#steps].text_bot = str or ""
+ end
+end
+
+function commands.step_cell_top(str)
+ if #steps > 0 then
+ steps[#steps].cell_top = str or ""
+ end
+end
+
+function commands.step_cell_bot(str)
+ if #steps > 0 then
+ steps[#steps].cell_bot = str or ""
+ end
+end
diff --git a/tex/context/modules/mkiv/m-steps.mkvi b/tex/context/modules/mkiv/m-steps.mkvi
new file mode 100644
index 000000000..c9c5a0636
--- /dev/null
+++ b/tex/context/modules/mkiv/m-steps.mkvi
@@ -0,0 +1,382 @@
+%D \module
+%D [ file=m-steps,
+%D version=2011.10.07, % 2001.05.28,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Step Charts \& Tables,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% multiple texts?
+
+\registerctxluafile{m-steps}{}
+
+\unprotect
+
+\installcorenamespace {stepcharts}
+\installcorenamespace {steptables}
+\installcorenamespace {stepcells}
+\installcorenamespace {steptexts}
+\installcorenamespace {steplines}
+
+\installsimplecommandhandler \??stepcharts {STEPchart} \??stepcharts
+\installsimplecommandhandler \??steptables {STEPtable} \??steptables
+\installsimplecommandhandler \??stepcells {STEPcell} \??stepcells
+\installsimplecommandhandler \??steptexts {STEPtext} \??steptexts
+\installsimplecommandhandler \??steplines {STEPline} \??steplines
+
+\let\setupSTEPcharts\setupSTEPchart
+\let\setupSTEPtables\setupSTEPtable
+\let\setupSTEPcells \setupSTEPcell
+\let\setupSTEPtexts \setupSTEPtext
+\let\setupSTEPlines \setupSTEPline
+
+% numeric text_text_distance ; text_text_distance := 20pt ;
+% numeric step_distance ; step_distance := 20pt ;
+
+\setupSTEPcharts
+ [\c!alternative=\v!horizontal,
+ \c!before=\blank,
+ \c!after=\blank]
+% \c!offset=.15\bodyfontsize
+% \c!height=2ex
+
+% \setupSTEPtables
+% [\c!before=\blank,
+% \c!after=\blank,
+% \c!distance=.25em,
+% \c!voffset=1ex,
+% \c!method=1,
+% \c!width=4em,
+% \c!offset=.15\bodyfontsize]
+
+\setupSTEPcells
+ [\c!alternative=1, % TODO
+% \c!style=,
+% \c!color=,
+ \c!dx=\bodyfontsize,
+ \c!dy=\bodyfontsize,
+ \c!background=\v!color,
+ \c!backgroundcolor=STEPbackgroundcolor,
+ \c!rulethickness=.1\bodyfontsize,
+ \c!framecolor=STEPframecolor,
+ \c!offset=.25\bodyfontsize]
+
+\setupSTEPtexts
+ [\c!alternative=1, % TODO
+% \c!style=\v!smallbodyfont,
+% \c!color=,
+ \c!background=\v!color,
+ \c!backgroundcolor=STEPbackgroundcolor,
+ \c!rulethickness=.1\bodyfontsize,
+ \c!framecolor=STEPframecolor,
+ \c!distance=.5\bodyfontsize,
+ \c!offset=.25\bodyfontsize]
+
+\setupSTEPlines
+ [\c!alternative=1, % TODO
+ \c!rulethickness=.15\bodyfontsize,
+ \c!height=3\bodyfontsize,
+ \c!distance=.5\bodyfontsize,
+ \c!offset=.25\bodyfontsize,
+ \c!color=STEPlinecolor]
+
+\definecolor [STEPlinecolor] [s=.5]
+\definecolor [STEPframecolor] [s=.7]
+\definecolor [STEPbackgroundcolor] [s=.9]
+
+\newtoks\everySTEPchart
+
+\unexpanded\def\startSTEPchart
+ {\begingroup
+ \dodoubleempty\module_steps_start_chart}
+
+\def\module_steps_start_chart[#name][#settings]%
+ {\startnointerference
+ \iffirstargument
+ \doifelseassignment{#name}
+ {\let\currentSTEPchart\empty
+ \xdef\module_steps_flush_chart{\module_steps_chart[][#name]}}
+ {\edef\currentSTEPchart{#name}%
+ \glet\module_steps_flush_chart\relax}% settings are not stored
+ \else
+ \let\currentSTEPchart\empty
+ \gdef\module_steps_flush_chart{\module_steps_chart[][]}%
+ \fi
+ \the\everySTEPchart
+ \ctxcommand{step_start_chart("\currentSTEPchart")}}
+
+\unexpanded\def\stopSTEPchart
+ {\ctxcommand{step_stop_chart()}%
+ \stopnointerference
+ \module_steps_flush_chart
+ \endgroup}
+
+\unexpanded\def\startSTEPtable
+ {\begingroup
+ \setupSTEPchart[\c!alternative=\v!vertical]%
+ \dodoubleempty\module_steps_start_chart}
+
+\let\stopSTEPtable\stopSTEPchart
+
+\let\module_steps_flush_chart\relax
+
+\unexpanded\def\STEPchart
+ {\dodoubleempty\module_steps_chart}
+
+\unexpanded\def\module_steps_chart[#name][#settings]%
+ {\begingroup
+ \setupSTEPchart[#settings]%
+ \ifinsidefloat
+ \makeSTEPchart[#name]%
+ \else
+ \STEPchartparameter\c!before
+ \startbaselinecorrection
+ \setlocalhsize
+ \makeSTEPchart[#name]%
+ \stopbaselinecorrection
+ \STEPchartparameter\c!after
+ \fi
+ \endgroup}
+
+\unexpanded\def\makeSTEPchart[#name]%
+ {\ctxcommand{step_make_chart {
+ chart = {
+ name = "#name",
+ alternative = "\STEPchartparameter\c!alternative",
+ },
+ cell = {
+ alternative = "\STEPcellparameter\c!alternative", % TODO: shapes
+ offset = \number\dimexpr\STEPcellparameter\c!offset,
+ rulethickness = \number\dimexpr\STEPcellparameter\c!rulethickness,
+ framecolor = "\STEPcellparameter\c!framecolor",
+ backgroundcolor = "\STEPcellparameter\c!backgroundcolor",
+ dx = \number\dimexpr\STEPcellparameter\c!dx,
+ dy = \number\dimexpr\STEPcellparameter\c!dy,
+ },
+ text = {
+ alternative = "\STEPtextparameter\c!alternative", % TODO: shapes
+ offset = \number\dimexpr\STEPtextparameter\c!offset,
+ distance = \number\dimexpr\STEPtextparameter\c!distance,
+ rulethickness = \number\dimexpr\STEPtextparameter\c!rulethickness,
+ framecolor = "\STEPtextparameter\c!framecolor",
+ backgroundcolor = "\STEPtextparameter\c!backgroundcolor",
+ },
+ line = {
+ alternative = "\STEPlineparameter\c!alternative", % TODO: dash, arrow
+ rulethickness = \number\dimexpr\STEPlineparameter\c!rulethickness,
+ height = \number\dimexpr\STEPlineparameter\c!height,
+ distance = \number\dimexpr\STEPlineparameter\c!distance,
+ offset = \number\dimexpr\STEPlineparameter\c!offset,
+ color = "\STEPlineparameter\c!color",
+ },
+ }}}
+
+\unexpanded\def\startSTEPcell
+ {\ctxcommand{step_start_cell()}}
+
+\unexpanded\def\stopSTEPcell
+ {\ctxcommand{step_stop_cell()}}
+
+\unexpanded\def\module_steps_cells#top#bot%
+ {\ctxcommand{step_cells(\!!bs\detokenize{#top}\!!es,\!!bs\detokenize{#bot}\!!es)}}
+
+\unexpanded\def\module_steps_texts#top#bot%
+ {\ctxcommand{step_texts(\!!bs\detokenize{#top}\!!es,\!!bs\detokenize{#bot}\!!es)}}
+
+\unexpanded\def\module_steps_cell#str%
+ {\ctxcommand{step_cell(\!!bs\detokenize{#str}\!!es)}}
+
+\unexpanded\def\module_steps_text#str%
+ {\ctxcommand{step_text(\!!bs\detokenize{#str}\!!es)}}
+
+\unexpanded\def\module_steps_textset#left#middle#right%
+ {\ctxcommand{step_textset(\!!bs\detokenize{#left}\!!es,\!!bs\detokenize{#middle}\!!es,\!!bs\detokenize{#right}\!!es)}}
+
+\unexpanded\def\module_steps_toptext#top%
+ {\ctxcommand{step_text_top(\!!bs\detokenize{#top}\!!es)}}
+
+\unexpanded\def\module_steps_bottext#bot%
+ {\ctxcommand{step_text_bot(\!!bs\detokenize{#bot}\!!es)}}
+
+\unexpanded\def\module_steps_topcell#top%
+ {\ctxcommand{step_cell_top(\!!bs\detokenize{#top}\!!es)}}
+
+\unexpanded\def\module_steps_botcell#bot%
+ {\ctxcommand{step_cell_bot(\!!bs\detokenize{#bot}\!!es)}}
+
+\appendtoks
+ \let\cells \module_steps_cells
+ \let\texts \module_steps_texts
+ \let\cell \module_steps_cell
+ \let\text \module_steps_text
+ \let\textset\module_steps_textset
+ \let\toptext\module_steps_toptext
+ \let\bottext\module_steps_bottext
+ \let\topcell\module_steps_topcell
+ \let\botcell\module_steps_botcell
+\to \everySTEPchart
+
+% todo: mapping can be done in lua
+
+\startxmlsetups xml:step:define
+ \xmlsetsetup{#1} {stepchart|steptable} {xml:step:*}
+\stopxmlsetups
+
+\xmlregistersetup{xml:step:define}
+
+\startxmlsetups xml:step:stepchart
+ \startSTEPchart
+ \xmlfilter{#1}{/(cells|texts|stepcell|cell|text)/command(xml:step:*)}
+ \stopSTEPchart
+\stopxmlsetups
+
+\startxmlsetups xml:step:steptable
+ \startSTEPtable
+ \xmlfilter{#1}{/(cells|texts|stepcell|cell|text)/command(xml:step:*)}
+ \stopSTEPtable
+\stopxmlsetups
+
+\startxmlsetups xml:step:cells
+ \cells {\xmltext{#1}{/top}} {\xmltext{#1}{/bot}}
+\stopxmlsetups
+
+\startxmlsetups xml:step:texts
+ \texts {\xmltext{#1}{/top}} {\xmltext{#1}{/bot}}
+\stopxmlsetups
+
+\startxmlsetups xml:step:stepcell
+ \cells {\xmltext{#1}{/topcell}} {\xmltext{#1}{/botcell}}
+ \texts {\xmltext{#1}{/toptext}} {\xmltext{#1}{/bottext}}
+\stopxmlsetups
+
+\startxmlsetups xml:step:cell
+ \cell {\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups xml:step:text
+ \text {\xmlflush{#1}}
+\stopxmlsetups
+
+\protect
+
+\continueifinputfile{m-steps.mkvi}
+
+\starttext
+
+\startbuffer
+<stepchart>
+ <cell> some cell </cell>
+ <text> some text </text>
+ <cell> some cell </cell>
+ <text> some text </text>
+ <cell> some cell </cell>
+</stepchart>
+\stopbuffer
+
+\typebuffer \processxmlbuffer
+
+
+\startbuffer
+<steptable>
+ <cell> some cell </cell>
+ <text> some text </text>
+ <cell> some cell </cell>
+ <text> some text </text>
+ <cell> some cell </cell>
+</steptable>
+\stopbuffer
+
+\typebuffer \processxmlbuffer
+
+\page
+
+\startSTEPchart
+ \cells {A} {B}
+ \cells {one} {five} \texts{$+2$}{$-2$}
+ \cells {two} {four} \texts{$+3$}{$-3$}
+ \cells {three} {three} \texts{$+4$}{$-4$}
+ \cells {four} {two} \texts{$+5$}{$-5$}
+ \cells {five} {one}
+\stopSTEPchart
+
+\startSTEPtable
+ \cell {one} \textset{$x$} {=}{$a+b+c$}
+ \cell {two} \textset{$c+d$}{=}{$y$}
+ \cell {three}
+\stopSTEPtable
+
+
+\page
+
+\startbuffer
+<stepchart>
+ <cells> <top> some text </top> <bot> some text </bot> </cells>
+ <texts> <top> text </top> <bot> text </bot> </texts>
+ <cells> <top> some text </top> <bot> some text </bot> </cells>
+</stepchart>
+\stopbuffer
+
+\processxmlbuffer
+
+\startbuffer
+<stepchart>
+ <cell> some text </cell>
+ <texts> <top> text </top> <bot> text </bot> </texts>
+ <cell> some text </cell>
+</stepchart>
+\stopbuffer
+
+\processxmlbuffer
+
+\page
+
+\startSTEPchart[second]
+ \startSTEPcell
+ \topcell {A}
+ \botcell {B}
+ \startSTEPcell
+ \topcell {one}
+ \botcell {five}
+ \toptext {$+2$}
+ \bottext {$-2$}
+ \stopSTEPcell
+ \startSTEPcell
+ \topcell {two}
+ \botcell {four}
+ \toptext {$+3$}
+ \bottext {$-3$}
+ \stopSTEPcell
+ \startSTEPcell
+ \topcell {three}
+ \botcell {three}
+ \toptext {$+4$}
+ \bottext {$-4$}
+ \stopSTEPcell
+ \startSTEPcell
+ \topcell {four}
+ \botcell {two}
+ \toptext {$+5$}
+ \bottext {$-5$}
+ \stopSTEPcell
+ \startSTEPcell
+ \topcell {five}
+ \botcell {one}
+ \stopSTEPcell
+\stopSTEPchart
+
+\STEPchart[first][alternative=horizontal]
+\STEPchart[first][alternative=vertical]
+
+\page
+
+\STEPchart[second][alternative=horizontal]
+\STEPchart[second][alternative=vertical]
+
+\page
+
+\stoptext
diff --git a/tex/context/modules/mkiv/m-subsub.mkiv b/tex/context/modules/mkiv/m-subsub.mkiv
new file mode 100644
index 000000000..88be11680
--- /dev/null
+++ b/tex/context/modules/mkiv/m-subsub.mkiv
@@ -0,0 +1,76 @@
+%D \module
+%D [ file=m-subsub,
+%D version=2000.12.14,
+%D title=\CONTEXT\ Private Modules,
+%D subtitle=More Section Levels,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. This module is not public.
+
+\unprotect
+
+\definesection[\s!section-8]
+\definesection[\s!section-9]
+\definesection[\s!section-10]
+\definesection[\s!section-11]
+\definesection[\s!section-12]
+
+\definehead
+ [\v!subsubsubsubsubsection]
+ [\c!section=\s!section-8,
+ \c!default=\v!subsubsubsubsection]
+
+\definehead
+ [\v!subsubsubsubsubsubsection]
+ [\c!section=\s!section-9,
+ \c!default=\v!subsubsubsubsubsection]
+
+\definehead
+ [\v!subsubsubsubsubsubsubsection]
+ [\c!section=\s!section-10,
+ \c!default=\v!subsubsubsubsubsubsection]
+
+\definehead
+ [\v!subsubsubsubsubsubsubsubsection]
+ [\c!section=\s!section-11,
+ \c!default=\v!subsubsubsubsubsubsubsection]
+
+\definehead
+ [\v!subsubsubsubsubsubsubsubsubsection]
+ [\c!section=\s!section-12,
+ \c!default=\v!subsubsubsubsubsubsubsubsection]
+
+\definehead
+ [\v!subsubsubsubsubsubject]
+ [\c!coupling=\v!subsubsubsubsubsection,
+ \c!default=\v!subsubsubsubsubsection,
+ \c!incrementnumber=\v!no]
+
+\definehead
+ [\v!subsubsubsubsubsubsubject]
+ [\c!coupling=\v!subsubsubsubsubsubsection,
+ \c!default=\v!subsubsubsubsubsubsection,
+ \c!incrementnumber=\v!no]
+
+\definehead
+ [\v!subsubsubsubsubsubsubsubject]
+ [\c!coupling=\v!subsubsubsubsubsubsubsection,
+ \c!default=\v!subsubsubsubsubsubsubsection,
+ \c!incrementnumber=\v!no]
+
+\definehead
+ [\v!subsubsubsubsubsubsubsubsubject]
+ [\c!coupling=\v!subsubsubsubsubsubsubsubsection,
+ \c!default=\v!subsubsubsubsubsubsubsubsection,
+ \c!incrementnumber=\v!no]
+
+\definehead
+ [\v!subsubsubsubsubsubsubsubsubsubject]
+ [\c!coupling=\v!subsubsubsubsubsubsubsubsubsection,
+ \c!default=\v!subsubsubsubsubsubsubsubsubsection,
+ \c!incrementnumber=\v!no]
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/m-timing.mkiv b/tex/context/modules/mkiv/m-timing.mkiv
new file mode 100644
index 000000000..5502768f6
--- /dev/null
+++ b/tex/context/modules/mkiv/m-timing.mkiv
@@ -0,0 +1,102 @@
+%D \module
+%D [ file=m-timing,
+%D version=2007.12.23,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Timing,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\ifdefined\ShowNamedUsage \endinput \fi
+
+%D Written at the end of 2007, this module is dedicated to Taco. Reaching this
+%D point in \LUATEX\ was a non trivial effort. By visualizing a bit what happens
+%D when pages come out of \LUATEX, you may get an idea what is involved. It took
+%D much time an dedication to reach this point in the development. Add to that
+%D those daily Skype intense discussion, testing and debugging moments. Time flies
+%D but progress is impressive. The motto of this module could be: what you see
+%D is what you get. An there is much more to come \unknown.
+
+% \usemodule[timing]
+% \setupcolors[state=start]
+% \starttext
+% \dorecurse{200}{\input tufte \par} \ShowUsage{}
+% \stoptext
+
+\definecolor[usage:line] [darkred]
+\definecolor[usage:time] [darkblue]
+\definecolor[usage:frame][darkgray]
+
+\ctxloadluafile{trac-tim}{}
+
+\startluacode
+local progress = moduledata.progress
+
+function progress.show(filename,parameters,nodes,other)
+ for n, name in pairs(parameters or progress.parameters()) do
+ context.ShowNamedUsage(filename or progress.defaultfilename,name,other or "")
+ end
+ for n, name in pairs(nodes or progress.nodes(filename)) do
+ context.ShowNamedUsage(filename or progress.defaultfilename,name,other or "")
+ end
+end
+\stopluacode
+
+% \everyfirstshipout
+
+\startnotmode[no-timing]
+ \appendtoks\ctxlua{moduledata.progress.store()}\to\everystarttext
+ \appendtoks\ctxlua{moduledata.progress.store()}\to\everyshipout
+ \ctxlua{luatex.registerstopactions(function() moduledata.progress.save() end)}
+\stopnotmode
+
+\unexpanded\def\ShowNamedUsage#1#2#3%
+ {\setbox\scratchbox\vbox\bgroup
+ \startMPcode
+ begingroup ; save p, q, b, h, w ;
+ path p, q, b ; numeric h, w ;
+ p := \cldcontext{moduledata.progress.path("#1","#2")} ;
+ % p := p shifted -llcorner p ;
+ if bbwidth(p) > 0 :
+ h := 100 ; w := 2 * h ;
+ w := \the\textwidth-3pt ; % correct for pen
+ p := p xstretched w ;
+ b := boundingbox (llcorner p -- llcorner p shifted (w,h)) ;
+ pickup pencircle scaled 3pt ; linecap := butt ;
+ draw b withcolor \MPcolor{usage:frame} ;
+ draw p withcolor \MPcolor{usage:line} ;
+ if ("#3" <> "") and ("#3" <> "#2") :
+ q := \cldcontext{moduledata.progress.path("#1","#3")} ;
+ % q := q shifted -llcorner q ;
+ if bbwidth(q) > 1 :
+ q := q xstretched w ;
+ pickup pencircle scaled 1.5pt ; linecap := butt ;
+ draw q withcolor \MPcolor{usage:time} ;
+ fi ;
+ fi ;
+ fi ;
+ endgroup ;
+ \stopMPcode
+ \egroup
+ \scratchdimen\wd\scratchbox
+ \ifdim\scratchdimen>\zeropoint
+ \startlinecorrection
+ \box\scratchbox \endgraf
+ \hbox to \scratchdimen{\tttf\strut\detokenize{#2}\hss
+ min:\cldcontext{moduledata.progress.bot("#1","\detokenize{#2}")}, %
+ max:\cldcontext{moduledata.progress.top("#1","\detokenize{#2}")}, %
+ pages:\cldcontext{moduledata.progress.pages("#1")}%
+ }%
+ \stoplinecorrection
+ \fi}
+
+\unexpanded\def\LoadUsage #1{\ctxlua{moduledata.progress.convert("#1")}}
+\unexpanded\def\ShowUsage #1{\ctxlua{moduledata.progress.show("#1",nil,nil,"elapsed_time")}}
+\unexpanded\def\ShowMemoryUsage#1{\ctxlua{moduledata.progress.show("#1",nil,{}, "elapsed_time")}}
+\unexpanded\def\ShowNodeUsage #1{\ctxlua{moduledata.progress.show("#1",{},nil, "elapsed_time")}}
+
+\endinput
diff --git a/tex/context/modules/mkiv/m-trackers.mkiv b/tex/context/modules/mkiv/m-trackers.mkiv
new file mode 100644
index 000000000..95c34b16f
--- /dev/null
+++ b/tex/context/modules/mkiv/m-trackers.mkiv
@@ -0,0 +1,3 @@
+\starttext
+ \showtrackers
+\stoptext
diff --git a/tex/context/modules/mkiv/m-translate.mkiv b/tex/context/modules/mkiv/m-translate.mkiv
new file mode 100644
index 000000000..2e6cbe950
--- /dev/null
+++ b/tex/context/modules/mkiv/m-translate.mkiv
@@ -0,0 +1,127 @@
+%D \module
+%D [ file=m-translate,
+%D version=2008.10.09,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Translations,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D We can make this module more clever (wildcards and such) but since
+%D it's only a demo we stick to the simple case for now. After all, it's
+%D better to fix your source.
+
+\startluacode
+ local translators = { }
+
+ moduledata.translators = translators
+
+ local compiled, list = nil, nil
+
+ -- function translators.register(from,to)
+ -- local l = lpeg.P(from)/to
+ -- if not list then
+ -- list = l
+ -- else
+ -- list = list + l
+ -- end
+ -- compiled = nil
+ -- end
+ --
+ -- function translators.translate(s)
+ -- if list then
+ -- if not compiled then
+ -- compiled = lpeg.Cs((list + lpeg.P(1))^0)
+ -- end
+ -- return compiled:match(s)
+ -- else
+ -- return s
+ -- end
+ -- end
+
+ -- local function prepare()
+
+ function translators.register(from,to)
+ if not list then
+ list = { [from] = to }
+ else
+ list[from] = to
+ end
+ compiled = nil
+ end
+
+ function translators.translate(s)
+ if list then
+ if not compiled then
+ local tree = lpeg.utfchartabletopattern(list)
+ compiled = lpeg.Cs((tree/list + lpeg.patterns.utf8character)^0 * lpeg.P(-1)) -- the P(1) is needed in order to accept non utf
+ end
+ return compiled:match(s)
+ else
+ return s
+ end
+ end
+
+ local textlineactions = resolvers.openers.helpers.textlineactions
+
+ utilities.sequencers.appendaction(textlineactions,"after","moduledata.translators.translate")
+
+ function translators.enable()
+ utilities.sequencers.enableaction(textlineactions,"moduledata.translators.translate")
+ end
+
+ function translators.disable()
+ utilities.sequencers.disableaction(textlineactions,"moduledata.translators.translate")
+ end
+
+ function translators.reset(s)
+ translators.enable()
+ list, compiled = nil, nil
+ end
+
+ translators.disable()
+\stopluacode
+
+\unprotect
+
+\unexpanded\def\translateinput
+ {\dodoubleargument\module_translate_input}
+
+\def\module_translate_input[#1][#2]%
+ {\ctxlua{moduledata.translators.register(\!!bs#1\!!es,\!!bs#2\!!es)}}
+
+\unexpanded\def\resetinputtranslation
+ {\ctxlua{moduledata.translators.reset()}}
+
+\unexpanded\def\enableinputtranslation
+ {\ctxlua{moduledata.translators.enable()}}
+
+\unexpanded\def\disableinputtranslation
+ {\ctxlua{moduledata.translators.disable()}}
+
+\unexpanded\def\readtranslatedfile#1%
+ {\enableinputtranslation
+ \readfile{#1}\donothing\donothing
+ \disableinputtranslation}
+
+\protect
+
+\continueifinputfile{m-translate.mkiv}
+
+\starttext
+
+ \translateinput[Moica][Mojca]
+ % \translateinput[Idris][Idris (aka ادريس)]
+ \translateinput[Idris][Idris (aka <something arabic here>)]
+
+ \enableinputtranslation
+
+ Well, it's not that hard to satisfy Idris' and Moicas \TEX\ needs.
+
+ \readtranslatedfile{tufte}
+
+\stoptext
diff --git a/tex/context/modules/mkiv/m-units.mkiv b/tex/context/modules/mkiv/m-units.mkiv
new file mode 100644
index 000000000..b0db8d548
--- /dev/null
+++ b/tex/context/modules/mkiv/m-units.mkiv
@@ -0,0 +1,912 @@
+%D \module
+%D [ file=m-units,
+%D version=1997.03.19,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=Scientific Units,
+%D author={Hans Hagen \& Ton Otten},
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Best use the built in unit handle!
+
+%D Scientific units can be typeset in math mode pretty well,
+%D but occasionally one has to take care of spacing.
+%D Furthermore, entering these units is not that natural as
+%D wanted. Therefore this module presents a more natural way of
+%D doing things, like:
+%D
+%D \starttyping
+%D 1.23 \Cubic \Meter \Per \Second
+%D \stoptyping
+%D
+%D This example shows that we use the order in which we say
+%D things, instead of typeset things. There is a separate
+%D manual for this module.
+
+%D Message number~1 deals with overruling the \type {\Degrees}
+%D macro defined in the core modules. Let's say that this is
+%D an upward compatibility issue.
+
+\startmessages dutch library: units
+ title: eenheden
+ 1: gebruik \string\Degrees\space\string\Celsius\space in plaats van \string\Celsius !
+\stopmessages
+
+\startmessages english library: units
+ title: units
+ 1: use \string\Degrees\space\string\Celsius\space instead of \string\Celsius !
+\stopmessages
+
+\startmessages german library: units
+ title: Einheiten
+ 1: Verwende \string\Degrees\space\string\Celsius\space statt \string\Celsius !
+\stopmessages
+
+\startmessages italian library: units
+ title: unita
+ 1: usare \string\Degrees\space\string\Celsius\space invece di \string\Celsius !
+\stopmessages
+
+\startmessages norwegian library: units
+ title: enheter
+ 1: bruk \string\Degrees\space\string\Celsius\space istedenfor \string\Celsius !
+\stopmessages
+
+\startmessages romanian library: units
+ title: unitati
+ 1: folositi \string\Degrees\space\string\Celsius\space in locul \string\Celsius !
+\stopmessages
+
+\startmessages french library: units
+ title: unitas
+ 1: utilisez \string\Degrees\space\string\Celsius\space A la place de \string\Celsius !
+\stopmessages
+
+\registerctxluafile{x-mathml}{}
+
+\unprotect
+
+\writestatus{\m!units}{The units module is obsolete because functionality is built into the core.}
+
+\let\unit\undefined
+
+\definesynonyms
+ [\v!unit]
+ [\v!units]
+ [\unitmeaning]
+
+\setupsynonyms
+ [\v!unit]
+ [\c!synonymcommand=\dimension]
+
+\startmodule[units]
+
+%D This runtime loadable module implements a way of defining
+%D units. The core macro is \type {\dimension}, a rather clever
+%D one that is able to cooperate with some other dimension
+%D related macros. As said, this module enables user to enter:
+%D
+%D \starttyping
+%D some 10 \Square \Meter \Per \Second or more
+%D \stoptyping
+%D
+%D The units itself are implemented as synonyms.
+%D
+%D \starttyping
+%D \definesynonyms [unit] [units] [\unitmeaning]
+%D \setupsynonyms [unit] [textstyle=\dimension]
+%D \stoptyping
+%D
+%D This definition means that we can ask for the meaning of a
+%D unit using \type {\unitmeaning} and get a list of used
+%D units by saying \type {\placelistofunits}
+%D
+%D We have to use the command \type {\unitmeaning} instead
+%D of \type {\meaning}, simply because the latter is a \TEX\
+%D primitive we don't want to loose. We use the label text
+%D mechanism for translations.
+
+%D \macros
+%D {dimension}
+%D
+%D The core of this module is the low level macro \type
+%D {\dimension}. Before presenting this macro, it's best to
+%D look at some applications, because it's supposed to show
+%D some intelligence that can beter be understood from the
+%D context.
+%D
+%D The next useless examples show some of the cases we want
+%D to handle in a proper way.
+%D
+%D \starttyping
+%D ... 10 \Square \Meter \Per \Volt \
+%D ... 10 \Square \Meter \Volt \
+%D ... 10 \Meter \Volt \
+%D ... 10 \Milli \Square \Meter \Per \Volt \
+%D ... 10 \Square \Milli \Meter \Per \Volt \
+%D ... 10 \Meter \Times \Meter \
+%D ... 10 \Square \Meter \Times \Meter \
+%D ... 10 \Square \Milli \Meter \Times \Meter \
+%D \stoptyping
+
+%D \macros
+%D {mathematicstypeface, dimensiontypeface,
+%D dimensionhalfspace, dimensionbackspace}
+%D
+%D There are some low level constants, that can be changed
+%D when needed. Some day I will write a decent setup command.
+
+\def\mathematicstypeface{\rm}
+\def\dimensiontypeface {\tf}
+
+\def\dimensionhalfspace {\ifmmode\,\else\hskip+.1em\relax\fi}
+\def\dimensionbackspace {\ifmmode\!\else\hskip-.1em\relax\fi}
+
+%D \macros
+%D {smashdimensionpower}
+%D
+%D Sometimes the baseline distance is not enough to provide
+%D for superscripts, so we smash their height by default.
+
+\newif\ifsmashdimensionpower \smashdimensionpowertrue
+
+%D The dimension mechanism uses a lot of signals to keep
+%D track if the current state.
+
+\newsignal\dimensionsignal
+\newsignal\dimensionpowersignal
+\newsignal\dimensionmidfixsignal
+\newsignal\dimensionaddfixsignal
+
+\let\thedimensionprefix = \empty
+\let\thedimensionpower = \empty
+
+%D \macros
+%D {spaceddimensions,textdimensions}
+%D
+%D The actual definition of \type {\dimension} overruled the
+%D one in the core modules. The boolean can be used to
+%D force spacing between units. The rather ugly test prevents
+%D problems with nested dimensions.
+
+\newif\ifspaceddimensions \spaceddimensionsfalse % user switch
+\newif\iftextdimensions \textdimensionsfalse % user switch
+
+%D You can see the consequence of forcing text dimensions
+%D when you compare the following code:
+%D
+%D \starttyping
+%D {\rm test \Square \Meter \Per \Second\ ziezo\Degrees} \par
+%D {\ss test \Square \Meter \Per \Second\ ziezo} \par
+%D {\tt test \Square \Meter \Per \Second\ ziezo}
+%D
+%D \textdimensionstrue
+%D
+%D {\rm test \Square \Meter \Per \Second\ ziezo} \par
+%D {\ss test \Square \Meter \Per \Second\ ziezo} \par
+%D {\tt test \Square \Meter \Per \Second\ ziezo}
+%D \stoptyping
+
+\newif\ifnesteddimension \nesteddimensionfalse % local switch
+
+\def\dodimensionpower#1%
+ {\iftextdimensions\expandafter\high\else\expandafter\normalsuperscript\fi{#1}}
+
+\def\ustartmathmode {\iftextdimensions\else\expandafter\startmathmode \fi}
+\def\ustopmathmode {\iftextdimensions\else\expandafter\stopmathmode \fi}
+\def\umathematicstypeface{\iftextdimensions\else\expandafter\mathematicstypeface\fi}
+
+%D In forced text mode, we ignore spacing in monospaced fonts.
+
+\def\udimensionhalfspace {\dodimensionspace\dimensionhalfspace}
+\def\udimensionbackspace {\dodimensionspace\dimensionbackspace}
+
+\def\dodimensionspace
+ {\iftextdimensions
+ \begingroup
+ \setbox0\hbox{i}%
+ \setbox2\hbox{m}%
+ \ifdim\wd0=\wd2
+ \endgroup
+ \@EAEAEA\gobbleoneargument
+ \else
+ \endgroup
+ \fi
+ \fi}
+
+\unexpanded\def\dimension#1%
+ {\begingroup
+ \global\let\savedthedimensionprefix\thedimensionprefix
+ \global\let\savedthedimensionpower\thedimensionpower
+ \unexpanded\def\dimension##1{\global\nesteddimensiontrue}%
+ \let\dimensionprefix\dimension
+ \let\dimensionmidfix\dimension
+ \let\dimensionsuffix\dimension
+ \let\dimensionpower \dimension
+ \global\nesteddimensionfalse
+ \setbox\scratchbox\hbox{\ustartmathmode#1\ustopmathmode}% pre-roll
+ \global\let\thedimensionprefix\savedthedimensionprefix
+ \global\let\thedimensionpower \savedthedimensionpower
+ \endgroup
+ \ifnesteddimension#1\else\dodimension{#1}\fi}
+
+\def\dodimension#1%
+ {\dontbreakdimension
+ \ifdim\scratchdimen=\zeropoint\relax
+ \ifmmode
+ \udimensionhalfspace
+ \udimensionhalfspace
+ \fi
+ \ustartmathmode\dimensiontypeface
+ \else
+ \ustartmathmode\dimensiontypeface
+ \ifspaceddimensions
+ \ifdim\scratchdimen=\dimensionsignal\relax
+ \udimensionhalfspace
+ \else\ifdim\scratchdimen=\dimensionpowersignal\relax
+ \udimensionhalfspace
+ \fi
+ \fi
+ \fi
+ \fi
+ \umathematicstypeface\thedimensionprefix#1%
+ \ifx\thedimensionpower\empty
+ \else\ifsmashdimensionpower
+ \setbox\scratchbox=\hbox
+ {\iftextdimensions
+ \tx\thedimensionpower
+ \else
+ $\scriptstyle\thedimensionpower$%
+ \fi}%
+ \ht\scratchbox=\zeropoint
+ \dodimensionpower{\box\scratchbox}%
+ \else
+ \dodimensionpower{\thedimensionpower}%
+ \fi\fi
+ \ustopmathmode
+ % otherwise nobreak before space in 2 \Milli \Meter\ blabla
+ \doifnotmode{atpragma}{\nobreak}% this was always \nobreak
+ % only test this at pragma
+ \ifx\thedimensionpower\empty
+ \hskip\dimensionsignal
+ \else
+ \hskip\dimensionpowersignal
+ \fi
+ \global\let\thedimensionprefix\empty
+ \global\let\thedimensionpower\empty}
+
+%D \macros
+%D {dontbreakdimension,
+%D dimensionprefix, dimensionaddfix,
+%D dimensionnopfix, dimensionmidfix,
+%D dimensionpower}
+%D
+%D Here are some auxilliary macros.
+
+\def\dontbreakdimension
+ {\scratchdimen\lastskip
+ \unskip
+ \nobreak
+ \hskip\scratchdimen
+ \nobreak}
+
+\def\dimensionprefix#1%
+ {\gdef\thedimensionprefix{#1}}
+
+\def\dimensionaddfix#1%
+ {\unskip
+ %\mathematics{\umathematicstypeface#1}%
+ \ustartmathmode\umathematicstypeface#1\ustopmathmode
+ \nobreak
+ \hskip\dimensionaddfixsignal}
+
+\def\dimensionnopfix#1%
+ {\dontbreakdimension
+ \ifdim\scratchdimen=\dimensionpowersignal\relax
+ \ustartmathmode
+ \else
+ \ustartmathmode
+ \udimensionhalfspace
+ \nobreak
+ \fi
+ \umathematicstypeface#1%
+ \ustopmathmode
+ \nobreak
+ \hskip\dimensionsignal}
+
+\def\dimensionmidfix#1%
+ {\dontbreakdimension
+ \ifdim\scratchdimen=\dimensionpowersignal\relax
+ \ustartmathmode
+ \udimensionbackspace
+ \nobreak
+ \else
+ \ustartmathmode
+ \fi
+ \umathematicstypeface#1%
+ \ustopmathmode
+ \nobreak
+ \hskip\dimensionmidfixsignal}
+
+\def\dimensionpower#1%
+ {\gdef\thedimensionpower{#1}}
+
+%D \macros
+%D {SIunits, noSI, doSI}
+%D
+%D Some low level unit switching macros:
+
+\newif\ifSIunits \SIunitstrue
+
+\def\noSI#1{\begingroup\SIunitsfalse#1\endgroup}
+\def\doSI#1{\begingroup\SIunitstrue #1\endgroup}
+
+%D \macros
+%D {Degrees}
+%D
+%D We can fake the degrees symbol with:
+
+\def\Degrees{\dimensionaddfix{\mathematics{\normalsuperscript\circ}}}
+
+%D \macros
+%D {Unit, NoUnit}
+%D
+%D When a dimension has no leading number, we can use \type
+%D {\Unit}, and when no unit is appended, \type {\NoUnit} is
+%D to be used, just to prevent the prefix migrating to the
+%D next occasion.
+
+\def\Unit {\hskip\dimensionsignal}
+\def\NoUnit {\dimension{}}
+
+%D The mechanism described at the top of this module, depends
+%D on several dimensional components, like prefixes:
+
+\def\Atto {\dimensionprefix{a}}
+\def\Femto {\dimensionprefix{f}}
+\def\Pico {\dimensionprefix{p}}
+\def\Nano {\dimensionprefix{n}}
+\def\Micro {\dimensionprefix{\iftextdimensions u\else\mu\fi}}
+\def\Milli {\dimensionprefix{m}}
+\def\Centi {\dimensionprefix{c}}
+\def\Deci {\dimensionprefix{d}} % 10^{01}
+\def\Hecto {\dimensionprefix{h}} % 10^{02}
+\def\Kilo {\dimensionprefix{k}} % 10^{03}
+\def\Mega {\dimensionprefix{M}} % 10^{06}
+\def\Giga {\dimensionprefix{G}} % 10^{09}
+\def\Tera {\dimensionprefix{T}} % 10^{12}
+\def\Peta {\dimensionprefix{P}} % 10^{15}
+\def\Exa {\dimensionprefix{E}} % 10^{18}
+
+%def\Terra {\dimensionprefix{T}} % for old times sake
+
+\def\Kibi {\dimensionprefix{ki}} % 2^{10}
+\def\Mebi {\dimensionprefix{Mi}} % 2^{20}
+\def\Gibi {\dimensionprefix{Gi}} % 2^{30}
+\def\Tebi {\dimensionprefix{Ti}} % 2^{40}
+\def\Pebi {\dimensionprefix{Pi}} % 2^{50}
+
+%D and binary prefixes:
+
+\def\Kibi {\dimensionprefix{Ki}}
+\def\Mebi {\dimensionprefix{Mi}}
+\def\Gibi {\dimensionprefix{Gi}}
+\def\Tebi {\dimensionprefix{Ti}}
+\def\Pebi {\dimensionprefix{Pi}}
+\def\Exbi {\dimensionprefix{Ei}}
+\def\Zebi {\dimensionprefix{Zi}}
+\def\Yobi {\dimensionprefix{Yi}}
+
+%D and operators:
+
+\def\Times {\dimensionnopfix{\iftextdimensions.\else\cdot\fi}}
+\def\Solidus {\dimensionmidfix{/}}
+\def\Per {\dimensionmidfix{/}}
+\def\OutOf {\dimensionnopfix{:}}
+
+%D and suffixes:
+
+\def\Linear {\dimensionpower{1}}
+\def\Square {\dimensionpower{2}}
+\def\Cubic {\dimensionpower{3}}
+
+\def\Inverse {\dimensionpower{-1}}
+\def\ILinear {\dimensionpower{-1}}
+\def\ISquare {\dimensionpower{-2}}
+\def\ICubic {\dimensionpower{-3}}
+
+%D Apart from these components, the units themselves are
+%D defined using the synonym mechanism. First we define some
+%D length and volume related units.
+
+\getvalue{\v!unit} [Meter] {m} {meter}
+\getvalue{\v!unit} [pMeter] {\Pico \Meter} {picometer}
+\getvalue{\v!unit} [nMeter] {\Nano \Meter} {nanometer}
+\getvalue{\v!unit} [uMeter] {\Micro \Meter} {micrometer}
+\getvalue{\v!unit} [mMeter] {\Milli \Meter} {millimeter}
+\getvalue{\v!unit} [cMeter] {\Centi \Meter} {centimeter}
+\getvalue{\v!unit} [dMeter] {\Deci \Meter} {decimeter}
+\getvalue{\v!unit} [hMeter] {\Hecto \Meter} {hectometer}
+\getvalue{\v!unit} [kMeter] {\Kilo \Meter} {kilometer}
+
+%D After some discussion on the \CONTEXT\ mailing list in
+%D february 2002 it was decided to go from L to l for liters
+%D (Karel Wesselings alternative: \mathematics{\ell}).
+
+\getvalue{\v!unit} [Liter] {l} {liter}
+\getvalue{\v!unit} [mLiter] {\Milli \Liter} {milliliter}
+\getvalue{\v!unit} [cLiter] {\Centi \Liter} {centiliter}
+\getvalue{\v!unit} [dLiter] {\Deci \Liter} {deciliter}
+
+%D Next we define time related units (\type {\ifSI} still dutch only).
+
+\getvalue{\v!unit} [Sec] {s} {\labeltext{u:sec}}
+\getvalue{\v!unit} [fSec] {\Femto \Sec} {\labeltext{u:fsec}}
+\getvalue{\v!unit} [pSec] {\Pico \Sec} {\labeltext{u:psec}}
+\getvalue{\v!unit} [nSec] {\Nano \Sec} {\labeltext{u:nsec}}
+\getvalue{\v!unit} [uSec] {\Micro \Sec} {\labeltext{u:usec}}
+\getvalue{\v!unit} [mSec] {\Milli \Sec} {\labeltext{u:msec}}
+\getvalue{\v!unit} [Year] {\ifSIunits a \else j\fi} {\labeltext{u:year}}
+\getvalue{\v!unit} [Month] {m} {\labeltext{u:month}}
+\getvalue{\v!unit} [Week] {w} {\labeltext{u:week}}
+\getvalue{\v!unit} [Day] {d} {\labeltext{u:day}}
+\getvalue{\v!unit} [Hour] {\ifSIunits h \else u\fi} {\labeltext{u:hour}}
+\getvalue{\v!unit} [Min] {min} {\labeltext{u:min}}
+
+\setuplabeltext
+ [\s!nl]
+ [u:sec=seconde,
+ u:psec=picoseconde,
+ u:fsec=femtoseconde,
+ u:nsec=nanoseconde,
+ u:usec=microseconde,
+ u:msec=milliseconde,
+ u:year=jaar,
+ u:month=maand,
+ u:week=week,
+ u:day=dag,
+ u:hour=uur,
+ u:min=minuten]
+
+\setuplabeltext
+ [\s!en]
+ [u:sec=second,
+ u:fsec=femtosecond,
+ u:psec=picosecond,
+ u:nsec=nanosecond,
+ u:usec=microsecond,
+ u:msec=millisecond,
+ u:year=year,
+ u:month=month,
+ u:week=week,
+ u:day=day,
+ u:hour=hour,
+ u:min=minutes]
+
+\setuplabeltext
+ [\s!de]
+ [u:sec=Sekunde,
+ u:fsec=Femtosekunde,
+ u:psec=Picosekunde,
+ u:nsec=Nanosekunde,
+ u:usec=Microsekunde,
+ u:msec=Millisekunde,
+ u:year=Jahr,
+ u:month=Monat,
+ u:week=Woche,
+ u:day=Tag,
+ u:hour=Stunde,
+ u:min=Minuten]
+
+\setuplabeltext
+ [\s!hr]
+ [u:sec=sekunda,
+ u:fsec=femtosekunda,
+ u:psec=pikosekunda,
+ u:nsec=nanosekunda,
+ u:usec=mikrosekunda,
+ u:msec=milisekunda,
+ u:year=godina,
+ u:month=mjesec,
+ u:week=tjedan,
+ u:day=dan,
+ u:hour=sat,
+ u:min=minuta]
+
+\setuplabeltext
+ [\s!it]
+ [u:sec=secondo,
+ u:fsec=femtosecondo,
+ u:psec=picosecondo,
+ u:nsec=nanosecondo,
+ u:usec=microsecondo,
+ u:msec=millisecondo,
+ u:year=anno,
+ u:month=mese,
+ u:week=settimana,
+ u:day=giorno,
+ u:hour=ora,
+ u:min=minuti]
+
+%D Then we define some angles.
+
+\getvalue{\v!unit} [Rad] {rad} {\labeltext{u:rad}}
+\getvalue{\v!unit} [Deg] {{\mathematics{\normalsuperscript\circ}}} {\labeltext{u:deg}}
+
+\setuplabeltext
+ [\s!nl]
+ [u:rad=hoek radialen,
+ u:deg=hoek graden]
+
+\setuplabeltext
+ [\s!en]
+ [u:rad=angle radians,
+ u:deg=angle degrees]
+
+\setuplabeltext
+ [\s!de]
+ [u:rad=Bogenma\SS,
+ u:deg=Gradma\SS]
+
+\setuplabeltext
+ [\s!hr]
+ [u:rad=radijani,
+ u:deg=kutni stupnjevi]
+
+\setuplabeltext
+ [\s!it]
+ [u:rad=radianti,
+ u:deg=angoli sessagesimali]
+
+%D Rotation and frequency related units are defined by:
+
+\getvalue{\v!unit} [Hertz] {Hz} {Hertz}
+\getvalue{\v!unit} [kHertz] {\Kilo \Hertz} {kilo Hertz}
+\getvalue{\v!unit} [MHertz] {\Mega \Hertz} {mega Hertz}
+\getvalue{\v!unit} [GHertz] {\Giga \Hertz} {giga Hertz}
+\getvalue{\v!unit} [THertz] {\Tera \Hertz} {tera Hertz}
+\getvalue{\v!unit} [mHertz] {\Milli \Hertz} {milli Hertz}
+
+\getvalue{\v!unit} [RevPerSec] {RPS} {\labeltext{u:rps}}
+\getvalue{\v!unit} [RevPerMin] {RPM} {\labeltext{u:rpm}}
+
+\setuplabeltext
+ [\s!nl]
+ [u:rps=omwentelingen per seconde,
+ u:rpm=omwentelingen per minuut]
+
+\setuplabeltext
+ [\s!en]
+ [u:rps=revolutions per second,
+ u:rpm=revolutions per minute]
+
+\setuplabeltext
+ [\s!de]
+ [u:rps=Umdrehungen pro Sekunde,
+ u:rpm=Umdrehungen pro Minute]
+
+\setuplabeltext
+ [\s!hr]
+ [u:rps=okretaji po sekundi,
+ u:rpm=okretaji po minuti]
+
+\setuplabeltext
+ [\s!it]
+ [u:rps=giri al secondo,
+ u:rpm=giri al minuto]
+
+%D Mass and force:
+
+\getvalue{\v!unit} [Gram] {g} {gram}
+\getvalue{\v!unit} [uGram] {\Micro \Gram} {microgram}
+\getvalue{\v!unit} [mGram] {\Milli \Gram} {milligram}
+\getvalue{\v!unit} [kGram] {\Kilo \Gram} {kilogram}
+\getvalue{\v!unit} [Atom] {u} {\labeltext{u:u}}
+
+\getvalue{\v!unit} [Newton] {N} {Newton}
+\getvalue{\v!unit} [kNewton] {\Kilo \Newton} {kilo Newton}
+
+\getvalue{\v!unit} [Pascal] {Pa} {Pascal}
+\getvalue{\v!unit} [mPascal] {\Milli \Pascal} {milli Pascal}
+\getvalue{\v!unit} [kPascal] {\Kilo \Pascal} {kilo Pascal}
+
+\setuplabeltext
+ [\s!nl]
+ [u:u=atomaire massa eenheid]
+
+\setuplabeltext
+ [\s!en]
+ [u:u=atom mass unit]
+
+\setuplabeltext
+ [\s!de]
+ [u:u=Atomare Masseneinheit]
+
+\setuplabeltext
+ [\s!hr]
+ [u:u=unificirana atomska jedinica mase]
+
+\setuplabeltext
+ [\s!it]
+ [u:u=unit\`a di massa atomica]
+
+%D Energy units comes in two alternatives:
+
+\getvalue{\v!unit} [Joule] {J} {Joule}
+\getvalue{\v!unit} [mJoule] {\Milli \Joule} {milli Joule}
+\getvalue{\v!unit} [kJoule] {\Kilo \Joule} {kilo Joule}
+\getvalue{\v!unit} [MJoule] {\Mega \Joule} {mega Joule}
+\getvalue{\v!unit} [GJoule] {\Giga \Joule} {giga Joule}
+
+\getvalue{\v!unit} [Watt] {W} {Watt}
+\getvalue{\v!unit} [mWatt] {\Milli \Watt} {milli Watt}
+\getvalue{\v!unit} [kWatt] {\Kilo \Watt} {kilo Watt}
+\getvalue{\v!unit} [MWatt] {\Mega \Watt} {mega Watt}
+\getvalue{\v!unit} [GWatt] {\Giga \Watt} {giga Watt}
+\getvalue{\v!unit} [TWatt] {\Tera \Watt} {tera Watt}
+
+%D Although Celsius is no longer permitted, we define it by
+%D saying:
+
+\getvalue{\v!unit} [Celsius] {C} {Celsius}
+\getvalue{\v!unit} [Kelvin] {K} {Kelvin}
+\getvalue{\v!unit} [Fahrenheit] {F} {Fahrenheit}
+
+%D Some chemic related units are:
+
+\getvalue{\v!unit} [Mol] {mol} {mol}
+\getvalue{\v!unit} [mMol] {\Milli \Mol} {millimol}
+\getvalue{\v!unit} [kMol] {\Kilo \Mol} {kilomol}
+\getvalue{\v!unit} [Molair] {M} {molair (\Mol \Per \Liter)}
+\getvalue{\v!unit} [Equivalent] {eq} {equivalent}
+\getvalue{\v!unit} [mEquivalent] {\Milli \Equivalent} {milli equivalent}
+
+%D There are quite a lot units related to electricity and
+%D magnetism:
+
+\getvalue{\v!unit} [Farad] {F} {Farad}
+\getvalue{\v!unit} [pFarad] {\Pico \Farad} {pico Farad}
+\getvalue{\v!unit} [nFarad] {\Nano \Farad} {nano Farad}
+\getvalue{\v!unit} [uFarad] {\Micro \Farad} {micro Farad}
+\getvalue{\v!unit} [mFarad] {\Milli \Farad} {milli Farad}
+
+\getvalue{\v!unit} [Ohm] {\Omega} {Ohm}
+\getvalue{\v!unit} [kOhm] {\Kilo \Ohm} {kilo Ohm}
+
+\getvalue{\v!unit} [Siemens] {S} {Siemens}
+
+\getvalue{\v!unit} [Ampere] {A} {Amp\`ere}
+\getvalue{\v!unit} [mAmpere] {\Milli \Ampere} {milli Amp\`ere}
+
+\getvalue{\v!unit} [Coulomb] {C} {Coulomb}
+
+\getvalue{\v!unit} [Volt] {V} {Volt}
+\getvalue{\v!unit} [mVolt] {\Milli \Volt} {milli Volt}
+\getvalue{\v!unit} [kVolt] {\Kilo \Volt} {kilo Volt}
+\getvalue{\v!unit} [eVolt] {eV} {electronvolt}
+\getvalue{\v!unit} [keVolt] {\Kilo \eVolt} {kilo electronvolt}
+\getvalue{\v!unit} [MeVolt] {\Mega \eVolt} {mega electronvolt}
+
+\getvalue{\v!unit} [Tesla] {T} {Tesla}
+
+\getvalue{\v!unit} [VoltAC] {V\normalsubscript{\xbox{ac}}} {\labeltext{u:vac}}
+\getvalue{\v!unit} [VoltDC] {V\normalsubscript{\xbox{dc}}} {\labeltext{u:vdc}}
+
+\setuplabeltext
+ [\s!nl]
+ [u:vac=wisselspanning,
+ u:vdc=gelijkspanning]
+
+\setuplabeltext
+ [\s!en]
+ [u:vac=alternating current,
+ u:vdc=direct current]
+
+\setuplabeltext
+ [\s!de]
+ [u:vac=Wechselspannung,
+ u:vdc=Gleichspannung]
+
+\setuplabeltext
+ [\s!hr]
+ [u:vac=izmjeni\ccaron ni napon,
+ u:vdc=istosmjerni napon]
+
+\setuplabeltext
+ [\s!it]
+ [u:vac=corrente alternata,
+ u:vdc=corrente continua]
+
+%D Network bandwidth is specified in Bits:
+
+\getvalue{\v!unit} [Bit] {bit} {\labeltext{u:bit}}
+\getvalue{\v!unit} [Baud] {Bd} {Baud (Bit/s)}
+
+%D Computer memory size is specified in Bytes:
+
+\getvalue{\v!unit} [Byte] {B} {\labeltext{u:byte}}
+\getvalue{\v!unit} [kByte] {\Kilo \Byte} {kilo Byte}
+\getvalue{\v!unit} [MByte] {\Mega \Byte} {mega Byte}
+\getvalue{\v!unit} [GByte] {\Giga \Byte} {giga Byte}
+\getvalue{\v!unit} [TByte] {\Tera \Byte} {tera Byte}
+
+\setuplabeltext
+ [\s!en]
+ [u:bit=Bit,
+ u:byte=Byte]
+
+\setuplabeltext
+ [\s!hr]
+ [u:bit=bit,
+ u:byte=bajt]
+
+%D Telecommunication call density is specified in Erlangs:
+
+\getvalue{\v!unit} [Erlang] {E} {Erlang}
+
+%D Some radiation related units:
+
+\getvalue{\v!unit} [Bequerel] {Bq} {Bequerel}
+\getvalue{\v!unit} [MBequerel] {\Mega \Bequerel} {Bequerel}
+\getvalue{\v!unit} [Sievert] {Sv} {Sievert}
+\getvalue{\v!unit} [mSievert] {\Milli \Sievert} {milli Sievert}
+
+%D Light:
+
+\getvalue{\v!unit} [Candela] {cd} {Candela}
+
+%D and some sound ones:
+
+\getvalue{\v!unit} [Bell] {B} {Bell}
+\getvalue{\v!unit} [dBell] {\Deci \Bell} {decibel}
+
+%D We also define some non||regular, sometimes even forbidden,
+%D units:
+
+\getvalue{\v!unit} [At] {at} {\labeltext{u:at}}
+\getvalue{\v!unit} [Atm] {atm} {\labeltext{u:atm}}
+\getvalue{\v!unit} [Bar] {bar} {bar (100 \Kilo \Pascal)}
+\getvalue{\v!unit} [EVolt] {eV} {electronvolt}
+\getvalue{\v!unit} [Foot] {ft} {\labeltext{u:ft}}
+\getvalue{\v!unit} [Inch] {inch} {\labeltext{u:inch}}
+\getvalue{\v!unit} [Cal] {cal} {\labeltext{u:cal}}
+\getvalue{\v!unit} [Force] {f} {\labeltext{u:f}}
+\getvalue{\v!unit} [kCal] {\Kilo \Cal} {\labeltext{u:kcal}}
+\getvalue{\v!unit} [Lux] {lux} {lux}
+
+
+\def\xPercent {\dimensionaddfix{\percent }}
+\def\xPromille{\dimensionaddfix{\promille}}
+
+\getvalue{\v!unit} [Percent] {\xPercent } {percent}
+\getvalue{\v!unit} [Permille] {\xPromille} {promille}
+\getvalue{\v!unit} [Promille] {\xPromille} {promille}
+
+%D Some more, thanks to Tobias:
+
+\getvalue{\v!unit} [Gray] {Gr} {Gray}
+\getvalue{\v!unit} [Weber] {Wb} {Weber}
+\getvalue{\v!unit} [Henry] {H} {Henry}
+\getvalue{\v!unit} [Sterant] {sr} {Sterant}
+\getvalue{\v!unit} [Angstrom] {\hbox{\Aring}} {\Aring ngstr\"om}
+\getvalue{\v!unit} [Gauss] {G} {Gauss}
+
+\setuplabeltext
+ [\s!nl]
+ [u:at=technische atmosfeer,
+ u:atm=fysische atmosfeer,
+ u:ft=voet,
+ u:cal=calorie,
+ u:f=kracht (force),
+ u:kcal=kilocalorie]
+
+\setuplabeltext
+ [\s!en]
+ [u:at=technical atmospheric pressure,
+ u:atm=physical atmospheric pressure,
+ u:ft=foot,
+ u:inch=inch,
+ u:cal=calory,
+ u:f=force,
+ u:kcal=kilocalory]
+
+\setuplabeltext
+ [\s!de]
+ [u:at=Technischer atmosph\"arischer Druck,
+ u:atm=physkalischer atmosph\"arischer Druck,
+ u:ft=Fu\SS,
+ u:cal=Kalorien,
+ u:f=Force,
+ u:kcal=Kilokalorien]
+
+\setuplabeltext
+ [\s!hr]
+ [u:at=tehni\ccaron ka atmosfera,
+ u:atm=standardna atmosfera,
+ u:ft=stopa,
+ u:inch=in\ccaron a,
+ u:cal=kalorija,
+ u:f=Force,
+ u:kcal=Kilokalorien]
+
+\setuplabeltext
+ [\s!it]
+ [u:at=pressione atmosferica tecnica,
+ u:atm=pressione atmosfera fisica,
+ u:ft=piede,
+ u:cal=caloria,
+ u:f=forza,
+ u:kcal=chilocaloria]
+
+%D Here are some old ones, still there for compatibility
+%D reasons. These will probably be obsolete in a few years.
+
+\def\MeterTwee {\Square \Meter}
+\def\mMeterTwee {\Square \Milli \Meter}
+\def\cMeterTwee {\Square \Centi \Meter}
+\def\dMeterTwee {\Square \Deci \Meter}
+\def\kMeterTwee {\Square \Kilo \Meter}
+
+\def\MeterDrie {\Cubic \Meter}
+\def\mMeterDrie {\Cubic \Milli \Meter}
+\def\cMeterDrie {\Kubic \Centi \Meter}
+\def\dMeterDrie {\Cubic \Deci \Meter}
+\def\kMeterDrie {\Cubic \Kilo \Meter}
+
+\def\LiterTwee {\Square \Liter}
+\def\SecTwee {\Square \Sec}
+\def\SecMinEen {\Inverse \Sec}
+
+%D To make ourselves happy, we define some dutch specific
+%D units:
+
+\startinterface dutch
+
+ \getvalue{\v!unit} [PaardenKracht] {pk} {paardenkracht}
+ \getvalue{\v!unit} [Duits] {D} {duits}
+ \getvalue{\v!unit} [Kwik] {Hg} {kwikkolom}
+ \getvalue{\v!unit} [Hectare] {ha} {hectare}
+ \getvalue{\v!unit} [kGramForce] {\Kilo \Gram \Force} {kilogramforce}
+ \getvalue{\v!unit} [kWattUur] {\Kilo \Watt \Uur} {kilowattuur}
+ \getvalue{\v!unit} [MeterKwik] {\Meter \Kwik} {meter kwikkolom}
+ \getvalue{\v!unit} [Waterkolom] {WK} {waterkolom}
+ \getvalue{\v!unit} [MeterWater] {\Meter \Waterkolom} {meter waterkolom}
+ \getvalue{\v!unit} [DrogeStof] {ds} {droge stof}
+ \getvalue{\v!unit} [Normaal] {N} {normaal}
+
+ \getvalue{\v!unit} [Ton] {t} {ton}
+ \getvalue{\v!unit} [kTon] {\Kilo \Ton} {kiloton}
+
+ \let \OmwPerSec \RevPerSec
+ \let \OmwPerMin \RevPerMin
+ \let \Graden \Deg
+ \let \PaardeKracht \PaardenKracht
+ \let \Atoom \Atom
+ \let \Heure \Hour
+ \let \Jaar \Year
+ \let \Maand \Month
+ \let \Dag \Day
+ \let \Uur \Hour
+
+\stopinterface
+
+%D Finally we define some equivalents. By using \type {\let}
+%D we can be sure that they don't end up double in the lists of
+%D units.
+
+\let \Second \Sec
+\let \Kubic \Cubic
+\let \IKubic \ICubic
+
+%D Option:
+
+% \def\Micro{\dimensionprefix{\iftextdimensions\mathematics\mu \else\mu \fi}}
+% \def\Times{\dimensionnopfix{\iftextdimensions\mathematics\cdot\else\cdot\fi}}
+
+\stopmodule
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/m-visual.mkiv b/tex/context/modules/mkiv/m-visual.mkiv
new file mode 100644
index 000000000..d50215966
--- /dev/null
+++ b/tex/context/modules/mkiv/m-visual.mkiv
@@ -0,0 +1,809 @@
+%D \module
+%D [ file=m-visual,
+%D version=2000.01.10,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=Visualization and Faking,
+%D author={Hans Hagen \& Ton Otten},
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+%D Much will probably be replaced by \LUA\ based solutions which is
+%D rather trivial and fun doing.
+
+%D This module collect a few more visual debugger features. I
+%D needed them for manuals and styles. The macros are documented
+%D in a my way document.
+
+\definecolor[fakerulecolor] [black]
+\definecolor[fakebaselinecolor] [green]
+\definecolor[fakeparindentcolor][blue]
+
+\newif\iffakebaseline \fakebaselinetrue
+
+\unexpanded\def\fakerule#1%
+ {\strut
+ \begingroup
+ \directcolored[fakerulecolor]%
+ \iffakebaseline
+ \vrule\s!height1.25ex\s!depth-.05ex\s!width#1%
+ \kern-#1%
+ \vrule\s!height-.05ex\s!depth .25ex\s!width#1%
+ \else
+ \vrule\s!height1.25ex\s!depth .25ex\s!width#1%
+ \fi
+ \endgroup
+ \allowbreak}
+
+\unexpanded\def\dorandomrecurse#1%
+ {\getrandomcount\scratchcounter{1}{#1}%
+ \dorecurse\scratchcounter}
+
+% can be used in hbox, so %'s are really needed
+
+\unexpanded\def\fakelines#1#2% min max / 3 10
+ {\fakeparindent
+ \scratchdimen\hsize
+ \ifindentation
+ \advance\scratchdimen -\parindent
+ \fi
+ \fakerule\scratchdimen\break
+ \getrandomcount\scratchcounter{\ifcase0#1 3\else#1\fi}{\ifcase0#2 10\else#2\fi}%
+ \dorecurse\scratchcounter{\fakerule\hsize}%
+ \getrandomdimen\scratchdimen{.25\hsize}\hsize
+ \fakerule\scratchdimen
+ \par} % indeed
+
+\unexpanded\def\fakewords
+ {\ifvmode\fakeparindent\fi\onlyfakewords}
+
+\definepalet
+ [fakerule]
+ [fr1c=darkred,
+ fr2c=darkgreen,
+ fr3c=darkblue,
+ fr4c=darkyellow,
+ fr5c=darkgray]
+
+\unexpanded\def\onlyfakewords#1#2% min max / 10 40
+ {\getrandomcount\scratchcounter{\ifcase0#1 10\else#1\fi}{\ifcase0#2 40\else#2\fi}%
+ \dofakewords\scratchcounter
+ } % no \par
+
+\unexpanded\def\fakenwords#1#2% words seed
+ {\fakeparindent
+ \getrandomseed\fakedwordseed
+ \setrandomseed{\ifcase0#2 #1\else#2\fi}%
+ \dofakewords{#1}%
+ \setrandomseed\fakedwordseed
+ } % no \par
+
+\def\dofakewords#1%
+ {\bgroup
+ \dorecurse{#1}
+ {\getrandomcount\scratchcounter{1}{5}%
+ \dorecurse\scratchcounter
+ {\getrandomdimen\scratchdimen{.5em}{1.25em}%
+ \fakerule\scratchdimen}%
+ \space}%
+ \removeunwantedspaces
+ \egroup}
+
+\def\doshowfakewords#1%
+ {\bgroup
+ \setuppalet[fakerule]%
+ \definecolor[fakerulecolor]%
+ \dorecurse{#1}
+ {\getrandomcount\scratchcounter{1}{5}%
+ \dorecurse\scratchcounter
+ {\getrandomdimen\scratchdimen{.5em}{1.25em}%
+ \color[fr\recurselevel c]{\fakerule\scratchdimen}}%
+ \space}%
+ \removeunwantedspaces
+ \egroup}
+
+\unexpanded\def\showfakewords
+ {\let\dofakewords\doshowfakewords}
+
+\unexpanded\def\fakeword
+ {\fakewords{1}{1}} % no \plusone
+
+\unexpanded\def\fakeparindent
+ {\noindent
+ \ifindentation
+ \ifx\dofakedroppedcaps\relax
+ {\fakeparindentcolor
+ \vrule
+ \s!height \strutheight % not longer .5ex
+ \s!depth \strutdepth % not longer 0pt
+ \s!width \parindent}%
+ \else
+ \dofakedroppedcaps \let\dofakedroppedcaps\relax
+ \fi
+% \else
+% \dontleavehmode
+ \fi}
+
+\let\dofakedroppedcaps\relax
+
+\unexpanded\def\fakedroppedcaps#1%
+ {\ifnum#1>0
+ \def\dofakedroppedcaps
+ {\setbox\scratchbox\hbox
+ {\setbox\scratchbox\hbox{W}%
+ \scratchdimen#1\lineheight
+ \advance\scratchdimen -\lineheight
+ \advance\scratchdimen \dp\strutbox
+ \vrule
+ \s!width#1\wd\scratchbox
+ \s!height\ht\scratchbox
+ \s!depth\scratchdimen}%
+ \ht\scratchbox\ht\strutbox
+ \dp\scratchbox\dp\strutbox
+ \hangindent\wd\scratchbox
+ \advance\hangindent .5em
+ \wd\scratchbox\hangindent
+ \hangafter-#1\noindent
+ \llap{\fakeparindentcolor\box\scratchbox}}%
+ \fi}
+
+\unexpanded\def\fakefigure
+ {\dodoubleempty\dofakefigure}
+
+\def\dofakefigure[#1][#2]#3#4#5#6% [] [] minwidth maxwidth minheight maxheight
+ {\getvalue{\e!place\v!figure}
+ [#1][#2]%
+ {\freezerandomseed
+ \let\endstrut\relax
+ \let\begstrut\relax
+ \doifelseinset{#1}{\v!left,\v!right}
+ {\fakewords{2}{4}}
+ {\fakewords{4}{10}}}%
+ {\doifinset{#1}{\v!left,\v!right}
+ {\dimen0=.75\dimen0
+ \ifdim\dimen0>.6\hsize \dimen0=.5\hsize\fi
+ \ifdim\dimen0<.3\hsize \dimen0=.3\hsize\fi}%
+ \framed
+ [\c!width=\dimen0,
+ \c!height=\dimen2,
+ \c!frame=\v!off,
+ \c!background=\v!color,
+ \c!backgroundcolor=fakeparindentcolor]
+ {\bf\white#1}}%
+ \defrostrandomseed}
+
+\unexpanded\def\fakeimage#1#2#3#4%
+ {\getrandomdimen{\dimen0}{#1}{#3}%
+ \getrandomdimen{\dimen2}{#2}{#4}%
+ \framed
+ [\c!width=\dimen0,
+ \c!height=\dimen2,
+ \c!frame=\v!off,
+ \c!background=\v!color,
+ \c!backgroundcolor=fakeparindentcolor]
+ {}}
+
+\unexpanded\def\fakeformula
+ {\dimen0\zeropoint
+ \getrandomcount\scratchcounter{3}{6}%
+ \dorecurse\scratchcounter
+ {\getrandomdimen\scratchdimen{0.5em}{1.5em}%
+ \mathord{\red\fakerule\scratchdimen}%
+ \ifnum\recurselevel<\scratchcounter+\fi
+ \advance\scratchdimen\dimen0}%
+ =\mathinner{\red\fakerule\scratchdimen}}
+
+\unexpanded\def\fakespacingformula
+ {\color[fakebaselinecolor]{\ruledbaseline}\fakeformula}
+
+%D test \type{\bodyfontgrid}\space test
+%D test \type{\emexgrid} \space test
+
+\unexpanded\def\smashedgrid
+ {\dosingleempty\dosmashedgrid}
+
+\def\dosmashedgrid[#1]%
+ {\hsmashed
+ {\setbox\scratchbox=\hbox
+ {\basegrid
+ [\c!nx=10,\c!ny=10,\c!dx=1,\c!dy=1,
+ \c!unit=\bodyfontsize,#1]}%
+ \hbox to \zeropoint
+ {\hss\lower.5\ht\scratchbox\box\scratchbox\hss}%
+ \hbox to \zeropoint
+ {\hss
+ \black\vrule\s!width6\linewidth\s!height3\linewidth\s!depth3\linewidth
+ \hss}}}
+
+\unexpanded\def\bodyfontgrid
+ {\hbox
+ {{\linewidth.1pt\yellow\smashedgrid[\c!nx=30,\c!ny=30,\c!scale=.3333]}%
+ {\linewidth.2pt\green \smashedgrid[\c!nx=20,\c!ny=20,\c!scale=.5]}%
+ {\linewidth.3pt\red \smashedgrid[\c!nx=10,\c!ny=10,\c!scale=1]}}}
+
+\unexpanded\def\emexgrid
+ {\hbox
+ {{\linewidth.15pt\green\smashedgrid[\c!nx=20,\c!ny=20,\c!unit=ex]}%
+ {\linewidth.15pt\red \smashedgrid[\c!nx=10,\c!ny=10,\c!unit=em]}}}
+
+%D For Mojca:
+
+\unexpanded\def\simplethesis
+ {\setupsystem[random=1234]
+ \title{\fakewords{3}{4}}
+ \placelist[chapter,section]
+ \dorecurse{6}
+ {\chapter{\fakewords{5}{10}}
+ \dorecurse{5}
+ {\section{\fakewords{2}{5}}
+ \dorecurse{2}
+ {\dorecurse{3}{\fakewords{100}{200}\endgraf}
+ \placefigure{\fakewords{8}{15}}{\fakeimage{5cm}{3cm}{10cm}{5cm}}
+ \dorecurse{2}{\fakewords{100}{200}\endgraf}}}}}
+
+%D Moved code:
+
+%D \module
+%D [ file=trac-vis, % was core-vis,
+%D version=1996.06.01,
+%D title=\CONTEXT\ Tracking Macros,
+%D subtitle=Visualization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This module adds some more visualization cues to the ones
+%D supplied in the support module.
+%D
+%D %\everypar dual character, \the\everypar and \everypar=
+%D %\hrule cannot be grabbed in advance, switches mode
+%D %\vrule cannot be grabbed in advance, switches mode
+%D %
+%D %\indent only explicit ones
+%D %\noindent only explicit ones
+%D %\par only explicit ones
+%D
+%D %\leftskip only if explicit one
+%D %\rightskip only if explicit one
+
+\writestatus{loading}{ConTeXt Tracking Macros / Visualization}
+
+\unprotect
+
+%D \macros
+%D {indent, noindent, par}
+%D
+%D \TeX\ acts upon paragraphs. In mosts documents paragraphs
+%D are separated by empty lines, which internally are handled as
+%D \type{\par}. Paragraphs can be indented or not, depending on
+%D the setting of \type{\parindent}, the first token of a
+%D paragraph and/or user suppressed or forced indentation.
+%D
+%D Because the actual typesetting is based on both explicit
+%D user and implicit system actions, visualization is only
+%D possible for the user supplied \type{\indent},
+%D \type{\noindent}, and \type{\par}. Other
+%D 'clever' tricks will quite certainly lead to more failures
+%D than successes, so we only support these three explicit
+%D primitives and one macro:
+
+\unexpanded\def\showparagraphcue#1#2#3#4#5%
+ {\bgroup
+ \scratchdimen#1\relax
+ \dontinterfere
+ \dontcomplain
+ %boxrulewidth5\testrulewidth
+ #3#4\relax
+ \setbox\scratchbox\normalhbox to \scratchdimen
+ {#2{\ruledhbox to \scratchdimen
+ {\vrule #5 20\testrulewidth \s!width \zeropoint
+ \normalhss}}}%
+ \smashbox\scratchbox
+ \normalpenalty\plustenthousand
+ \box\scratchbox
+ \egroup}
+
+\unexpanded\def\ruledhanging
+ {\ifdim\hangindent>\zeropoint
+ \ifnum\hangafter<\zerocount
+ \normalhbox
+ {%boxrulewidth5\testrulewidth
+ \setbox\scratchbox\ruledhbox to \hangindent
+ {\scratchdimen\strutht
+ \advance\scratchdimen \strutdp
+ \vrule
+ \s!width \zeropoint
+ \s!height \zeropoint
+ \s!depth -\hangafter\scratchdimen}%
+ \normalhskip-\hangindent
+ \smashbox\scratchbox
+ \raise\strutht\box\scratchbox}%
+ \fi
+ \fi}
+
+\unexpanded\def\ruledparagraphcues
+ {\bgroup
+ \dontcomplain
+ \normalhbox to \zeropoint
+ {\ifdim\leftskip>\zeropoint\relax
+ \showparagraphcue\leftskip\llap\relax\relax\!!depth
+ \normalhskip-\leftskip
+ \fi
+ \ruledhanging
+ \normalhskip\hsize
+ \ifdim\rightskip>\zeropoint\relax
+ \normalhskip-\rightskip
+ \showparagraphcue\rightskip\relax\relax\relax\!!depth
+ \fi}%
+ \egroup}
+
+\unexpanded\def\ruledpar
+ {\relax
+ \ifhmode
+ \showparagraphcue{40\testrulewidth}\relax\rightrulefalse\relax\s!height
+ \fi
+ \normalpar}
+
+\unexpanded\def\rulednoindent
+ {\relax
+ \normalnoindent
+ \ruledparagraphcues
+ \showparagraphcue{40\testrulewidth}\llap\leftrulefalse\relax\s!height}
+
+\unexpanded\def\ruledindent
+ {\relax
+ \normalnoindent
+ \ruledparagraphcues
+ \ifdim\parindent>\zeropoint
+ \showparagraphcue\parindent\relax\relax\relax\s!height
+ \else
+ \showparagraphcue{40\testrulewidth}\llap\relax\relax\s!height
+ \fi
+ \normalhskip\parindent}
+
+\unexpanded\def\dontshowimplicits
+ {\let\noindent \normalnoindent
+ \let\indent \normalindent
+ \let\par \normalpar}
+
+\unexpanded\def\showimplicits
+ {\testrulewidth \defaulttestrulewidth
+ \let\noindent \rulednoindent
+ \let\indent \ruledindent
+ \let\par \ruledpar}
+
+%D The next few||line examples show the four cues. Keep in
+%D mind that we only see them when we explicitly open or close
+%D a paragraph.
+%D
+%D \bgroup
+%D \def\voorbeeld#1%
+%D {#1Visualizing some \TeX\ primitives and Plain \TeX\
+%D macros can be very instructive, at least it is to me.
+%D Here we see {\tt\string#1} and {\tt\string\ruledpar} in
+%D action, while {\tt\string\parindent} equals
+%D {\tt\the\parindent}.\ruledpar}
+%D
+%D \showimplicits
+%D
+%D \voorbeeld \indent
+%D \voorbeeld \noindent
+%D
+%D \parindent=60pt
+%D
+%D \voorbeeld \indent
+%D \voorbeeld \noindent
+%D
+%D \startnarrower
+%D \voorbeeld \indent
+%D \voorbeeld \noindent
+%D \stopnarrower
+%D \egroup
+%D
+%D These examples also demonstrate the visualization of
+%D \type {\leftskip} and \type {\rightskip}. The macro
+%D \type {\nofruledbaselines} determines the number of lines
+%D shown.
+
+\newcounter\ruledbaselines
+
+\def\nofruledbaselines{3}
+
+\unexpanded\def\ruledbaseline
+ {\vrule \s!width \zeropoint
+ \bgroup
+ \dontinterfere
+ \doglobal\increment\ruledbaselines
+ \scratchdimen\nofruledbaselines\baselineskip
+ \setbox\scratchbox\normalvbox to 2\scratchdimen
+ {\leaders
+ \normalhbox
+ {\strut
+ \vrule
+ \s!height \testrulewidth
+ \s!depth \testrulewidth
+ \s!width 120\points}
+ \normalvfill}%
+ \smashbox\scratchbox
+ \advance\scratchdimen \strutheightfactor\baselineskip
+ \setbox\scratchbox\normalhbox
+ {\normalhskip -48\points
+ \normalhbox to 24\points
+ {\normalhss
+ {\ttxx\ruledbaselines}%
+ \normalhskip6\points}%
+ \raise\scratchdimen\box\scratchbox}%
+ \smashbox\scratchbox
+ \box\scratchbox
+ \egroup}
+
+\unexpanded\def\showbaselines
+ {\testrulewidth\defaulttestrulewidth
+ \EveryPar{\ruledbaseline}}
+
+%D \macros
+%D {showpagebuilder}
+%D
+%D The next tracing option probaly is only of use to me and a
+%D few \CONTEXT\ hackers.
+
+\unexpanded\def\showpagebuilder
+ {\EveryPar{\doshowpagebuilder}}
+
+\unexpanded\def\doshowpagebuilder
+ {\strut\llap
+ {\startcolor[blue]\vl
+ \high{\infofont v:\the\vsize }\vl
+ \high{\infofont g:\the\pagegoal }\vl
+ \high{\infofont t:\the\pagetotal}\vl
+ \stopcolor}}
+
+%D \macros
+%D {colormarkbox,rastermarkbox}
+%D
+%D This macro is used in the pagebody routine. No other use
+%D is advocated here.
+%D
+%D \starttyping
+%D \colormarkbox0
+%D \stoptyping
+
+\def\colormarkoffset{\cutmarkoffset}
+\def\colormarklength{\cutmarklength}
+
+\def\dodocolorrangeA#1%
+ {\fastcolored[#1]{\hrule\s!width3em\s!height\scratchdimen\s!depth\zeropoint}}
+
+\def\docolorrangeA#1 #2 %
+ {\vbox
+ {\hsize3em % \scratchdimen
+ \ifcase#1\or
+ \dodocolorrangeA{c=#2}\or
+ \dodocolorrangeA{m=#2}\or
+ \dodocolorrangeA{y=#2}\or
+ \dodocolorrangeA{m=#2,y=#2}\or
+ \dodocolorrangeA{c=#2,y=#2}\or
+ \dodocolorrangeA{c=#2,m=#2}\fi
+ \ifdim\scratchdimen>1ex
+ \vskip-\scratchdimen
+ \vbox to \scratchdimen
+ {\vss\hbox to 3em{\hss#2\hss}\vss}%
+ \fi}}
+
+\def\colorrangeA#1%
+ {\vbox
+ {\startcolor[\s!white]%
+ \scratchdimen\dimexpr(-\colormarklength*4+\tractempheight+\tractempdepth)/21\relax
+ \offinterlineskip
+ \docolorrangeA #1 1.00 \docolorrangeA #1 0.95
+ \docolorrangeA #1 0.75
+ \docolorrangeA #1 0.50
+ \docolorrangeA #1 0.25 \docolorrangeA #1 0.05
+ \docolorrangeA #1 0.00
+ \stopcolor}}
+
+\def\docolorrangeB #1 #2 #3 #4 #5 %
+ {\fastcolored
+ [\c!c=#2,\c!m=#3,\c!y=#4,\c!k=#5]
+ {\vrule\s!width\scratchdimen\s!height\colormarklength\s!depth\zeropoint}%
+ \ifdim\scratchdimen>2em
+ \hskip-\scratchdimen
+ \vbox to \colormarklength
+ {\vss\hbox to \scratchdimen{\hss#1\hss}\vss}%
+ \fi}
+
+\def\colorrangeB
+ {\hbox
+ {\startcolor[\s!white]%
+ \scratchdimen\dimexpr(-\colormarklength*\plustwo+\tractempwidth)/11\relax
+ \docolorrangeB .5~C .5 0 0 0
+ \docolorrangeB .5~M 0 .5 0 0
+ \docolorrangeB .5~Y 0 0 .5 0
+ \docolorrangeB .5~K 0 0 0 .5
+ \docolorrangeB C 1 0 0 0
+ \docolorrangeB G 1 0 1 0
+ \docolorrangeB Y 0 0 1 0
+ \docolorrangeB R 0 1 1 0
+ \docolorrangeB M 0 1 0 0
+ \docolorrangeB B 1 1 0 0
+ \docolorrangeB K 0 0 0 1
+ \stopcolor}}
+
+\def\docolorrangeC#1 %
+ {\fastcolored
+ [\c!s=#1]%
+ {\vrule\s!width\scratchdimen\s!height\colormarklength\s!depth\zeropoint}%
+ \ifdim\scratchdimen>2em
+ \hskip-\scratchdimen
+ \vbox to \colormarklength
+ {\vss\hbox to \scratchdimen{\hss#1\hss}\vss}%
+ \fi}
+
+\def\colorrangeC
+ {\hbox
+ {\startcolor[\s!white]%
+ \scratchdimen\dimexpr(-\colormarklength*2+\tractempwidth)/14\relax
+ \docolorrangeC 1 \docolorrangeC .95
+ \docolorrangeC .9 \docolorrangeC .85
+ \docolorrangeC .8 \docolorrangeC .75
+ \docolorrangeC .7
+ \docolorrangeC .6
+ \docolorrangeC .5
+ \docolorrangeC .4
+ \docolorrangeC .3
+ \docolorrangeC .2
+ \docolorrangeC .1
+ \docolorrangeC 0
+ \stopcolor}}
+
+\def\docolormarkbox#1#2%
+ {\tractempheight\ht#2%
+ \tractempdepth \dp#2%
+ \tractempwidth \wd#2%
+ \setbox#2\hbox
+ {\scratchdimen\dimexpr\colormarklength/2\relax
+ \forgetall
+ \ssxx
+ \setbox\scratchbox\vbox
+ {\offinterlineskip
+ \vskip\dimexpr-\colormarkoffset\scratchdimen-2\scratchdimen\relax
+ \ifcase#1\relax
+ \vskip\dimexpr\colormarklength+\scratchdimen+\tractempheight\relax
+ \else
+ \hbox to \tractempwidth{\hss\hbox{\colorrangeB}\hss}%
+ \vskip\colormarkoffset\scratchdimen
+ \vbox to \tractempheight
+ {\vss
+ \hbox to \tractempwidth
+ {\llap{\colorrangeA1\hskip\colormarkoffset\scratchdimen}\hfill
+ \rlap{\hskip\colormarkoffset\scratchdimen\colorrangeA4}}%
+ \vss
+ \hbox to \tractempwidth
+ {\llap{\colorrangeA2\hskip\colormarkoffset\scratchdimen}\hfill
+ \rlap{\hskip\colormarkoffset\scratchdimen\colorrangeA5}}%
+ \vss
+ \hbox to \tractempwidth
+ {\llap{\colorrangeA3\hskip\colormarkoffset\scratchdimen}\hfill
+ \rlap{\hskip\colormarkoffset\scratchdimen\colorrangeA6}}%
+ \vss}%
+ \fi
+ \vskip\colormarkoffset\scratchdimen
+ \hbox to \tractempwidth
+ {\hss\lower\tractempdepth\hbox{\colorrangeC}\hss}}%
+ \ht\scratchbox\tractempheight
+ \dp\scratchbox\tractempdepth
+ \wd\scratchbox\zeropoint
+ \box\scratchbox
+ \box#2}%
+ \wd#2\tractempwidth
+ \ht#2\tractempheight
+ \dp#2\tractempdepth}
+
+\unexpanded\def\colormarkbox {\docolormarkbox\plusone } % #1
+\unexpanded\def\rastermarkbox{\docolormarkbox\zerocount} % #1
+
+%D \macros
+%D {showwhatsits, dontshowwhatsits}
+%D
+%D \TEX\ has three so called whatsits: \type {\mark}, \type
+%D {\write} and \type {\special}. The first one keeps track of
+%D the current state at page boundaries, the last two are used
+%D to communicate to the outside world. Due to fact that
+%D especially \type {\write} is often used in conjunction with
+%D \type {\edef}, we can only savely support that one in \ETEX.
+%D
+%D \bgroup \showwhatsits \setupcolors[state=start]
+%D
+%D Whatsits show up \color[blue]{in color} and are
+%D characterized bij their first character.\footnote [some note]
+%D {So we may encounter \type {w}, \type {m} and \type{s}.}
+%D They are \writestatus{dummy}{demo}\color[yellow]{stacked}.
+%D
+%D \egroup
+
+\newif\ifimmediatewrite
+
+\let\supernormalmark \normalmark % mark may already been superseded
+\let\supernormalmarks \normalmarks % mark may already been superseded
+
+\unexpanded\def\showwhatsits
+ {\protected\def\normalmark {\visualwhatsit100+m\supernormalmark }%
+ \protected\def\normalmarks{\visualwhatsit100+m\supernormalmarks}%
+ \protected\def\special {\visualwhatsit0100s\normalspecial }%
+ \protected\def\write {\visualwhatsit001-w\normalwrite }%
+ \let\immediate\immediatewhatsit
+ \appendtoks\dontshowwhatsits\to\everystoptext}
+
+\unexpanded\def\immediatewhatsit
+ {\bgroup\futurelet\next\doimmediatewhatsit}
+
+\unexpanded\def\doimmediatewhatsit
+ {\ifx\next\write
+ \egroup\immediatewritetrue
+ \else
+ \egroup\expandafter\normalimmediate
+ \fi}
+
+\unexpanded\def\dontshowwhatsits
+ {\let\immediate \normalimmediate
+ \let\normalmark\supernormalmark
+ \let\special \normalspecial
+ \let\write \normalwrite}
+
+\unexpanded\def\visualwhatsit#1#2#3#4#5%
+ {\bgroup
+ \pushwhatsit
+ \dontinterfere
+ \dontcomplain
+ \dontshowcomposition
+ \dontshowwhatsits
+ \ttx
+ \ifvmode\donetrue\else\donefalse\fi
+ \setbox\scratchbox\hbox
+ {\ifdone
+ \colored[r=#1,g=#2,b=#3]{#5}% temp hack
+ \else
+ \colored[s=0]{#5}% temp hack
+ \fi}%
+ \setbox\scratchbox\hbox
+ {\ifdone
+ \colored[r=#1,g=#2,b=#3]{\vrule\s!width\wd\scratchbox}% temp hack
+ \else
+ \colored[s=0]{\vrule\s!width\wd\scratchbox}% temp hack
+ \fi
+ \hskip-\wd\scratchbox\box\scratchbox}%
+ \scratchdimen1ex
+ \setbox\scratchbox\hbox
+ {\ifdone\hskip\else\raise#4\fi\scratchdimen\box\scratchbox}%
+ \smashbox\scratchbox
+ \ifdone\nointerlineskip\fi
+ \box\scratchbox
+ \ifvmode\nointerlineskip\fi
+ \popwhatsit
+ \egroup
+ \ifimmediatewrite
+ \immediatewritefalse
+ \expandafter\normalimmediate
+ \fi}
+
+\unexpanded\def\pushwhatsit
+ {\ifzeropt\lastskip
+ \ifcase\lastpenalty
+ \ifzeropt\lastkern
+ \ifhmode
+ \let\popwhatsit\relax
+ \else
+ \edef\popwhatsit{\prevdepth\the\prevdepth}%
+ \fi
+ \else
+ \ifhmode
+ \edef\popwhatsit{\kern\the\lastkern}\unkern
+ \else
+ \edef\popwhatsit{\kern\the\lastkern\prevdepth\the\prevdepth}%
+ \kern-\lastkern
+ \fi
+ \fi
+ \else
+ \ifhmode
+ \edef\popwhatsit{\the\lastpenalty}%
+ \unpenalty
+ \else
+ \edef\popwhatsit{\penalty\the\lastpenalty\prevdepth\the\prevdepth}%
+ %\nobreak
+ \fi
+ \fi
+ \else
+ \ifhmode
+ \edef\popwhatsit{\hskip\the\lastskip}\unskip
+ \else
+ \edef\popwhatsit{\vskip\the\lastskip\prevdepth\the\prevdepth}%
+ \vskip-\lastskip
+ \fi
+ \fi}
+
+%D The next macro can be used to keep track of classes of
+%D boxes (handy for development cq.\ tracing).
+
+\def\dodotagbox#1#2#3% can be reimplemented
+ {\def\next##1##2##3##4%
+ {\vbox to \ht#2{##3\hbox to \wd#2{##1#3##2}##4}}%
+ \processaction
+ [#1]
+ [ l=>\next\relax\hfill\vfill\vfill,
+ r=>\next\hfill\relax\vfill\vfill,
+ t=>\next\hfill\hfill\relax\vfill,
+ b=>\next\hfill\hfill\vfill\relax,
+ lt=>\next\relax\hfill\relax\vfill,
+ lb=>\next\relax\hfill\vfill\relax,
+ rt=>\next\hfill\relax\relax\vfill,
+ rb=>\next\hfill\relax\vfill\relax,
+ tl=>\next\relax\hfill\relax\vfill,
+ bl=>\next\relax\hfill\vfill\relax,
+ tr=>\next\hfill\relax\relax\vfill,
+ br=>\next\hfill\relax\vfill\relax,
+ \s!default=>\next\hfill\hfill\vfill\vfill,
+ \s!unknown=>\next\hfill\hfill\vfill\vfill]}
+
+\def\dotagbox[#1]#2%
+ {\bgroup
+ \dowithnextbox
+ {\setbox\scratchbox\flushnextbox
+ \setbox\nextbox\ifhbox\nextbox\hbox\else\vbox\fi
+ \bgroup
+ \startoverlay
+ {\copy\scratchbox}
+ {\dodotagbox{#1}\scratchbox{\framed
+ [\c!background=\v!color,\c!backgroundcolor=\v!gray]{#2}}}
+ \stopoverlay
+ \egroup
+ \nextboxwd\the\wd\scratchbox
+ \nextboxht\the\ht\scratchbox
+ \nextboxdp\the\dp\scratchbox
+ \flushnextbox
+ \egroup}}
+
+\unexpanded\def\tagbox
+ {\dosingleempty\dotagbox}
+
+%D \macros
+%D {coloredhbox,coloredvbox,coloredvtop,
+%D coloredstrut}
+%D
+%D The following visualizations are used in some of the manuals:
+
+\definecolor[boxcolor:ht][r=.5,g=.75,b=.5]
+\definecolor[boxcolor:dp][r=.5,g=.5,b=.75]
+\definecolor[boxcolor:wd][r=.75,g=.5,b=.5]
+\definecolor[strutcolor] [r=.5,g=.25,b=.25]
+
+\unexpanded\def\coloredbox#1%
+ {\dowithnextbox{#1{\hbox
+ {\blackrule[\c!width=\nextboxwd,\c!height=\nextboxht,\c!depth=\zeropoint,\c!color=boxcolor:ht]%
+ \hskip-\nextboxwd
+ \blackrule[\c!width=\nextboxwd,\c!height=\zeropoint,\c!depth=\nextboxdp,\c!color=boxcolor:dp]%
+ \hskip-\nextboxwd
+ \box\nextbox}}}#1}
+
+\unexpanded\def\coloredhbox{\coloredbox\hbox}
+\unexpanded\def\coloredvbox{\coloredbox\vbox}
+\unexpanded\def\coloredvtop{\coloredbox\vtop}
+
+\unexpanded\def\coloredstrut
+ {\color[strutcolor]{\def\strutwidth{2\points}\setstrut\strut}}
+
+\protect
+
+\continueifinputfile{m-visual.mkiv}
+
+\starttext
+ \simplethesis
+\stoptext
diff --git a/tex/context/modules/mkiv/m-zint.mkiv b/tex/context/modules/mkiv/m-zint.mkiv
new file mode 100644
index 000000000..4957c8461
--- /dev/null
+++ b/tex/context/modules/mkiv/m-zint.mkiv
@@ -0,0 +1,112 @@
+%D \module
+%D [ file=m-zint,
+%D version=2010.12.07,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=Zint Barcode Generator,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Using \type {zint} seems to be the easiest way to generate
+%D (PDF417) barcodes so therefore we now have this module. There
+%D are proper (also windows) binaries at:
+%D
+%D \starttyping
+%D http://www.zint.org.uk
+%D \stoptyping
+%D
+%D There is a bit more code than needed as we want to be able to
+%D feed names.
+
+\startluacode
+
+moduledata.zint = { }
+
+local format, lower, gsub = string.format, string.lower, string.gsub
+local patterns = lpeg.patterns
+
+local zint = "zint" -- '"c:/program files/zint/zint.exe"'
+local defaultcode = "PDF417"
+
+local whitespace = patterns.whitespace
+local spaces = whitespace^0
+local key = (spaces / "") * patterns.digit^0 * (patterns.colon * spaces / "")
+local value = (whitespace / "" + (1 - key))^1
+local pattern = lpeg.Cf(lpeg.Ct("") * (lpeg.Cg((lpeg.Cs(key) / tonumber) * (lpeg.Cs(value) / lower)) + patterns.anything)^0,rawset)
+
+local reverse
+
+local function cleancode(code)
+ if not code or code == "" then
+ code = defaultcode
+ end
+ return lower(gsub(code," ",""))
+end
+
+local function numberofcode(code)
+ if not reverse then
+ local types = os.resultof(format("%s --types",zint)) or ""
+ local formats = lpeg.match(pattern,types)
+ if not formats or not next(formats) then
+ return code
+ end
+ reverse = table.swapped(formats) or { }
+ end
+ code = cleancode(code)
+ return reverse[code] or code
+end
+
+function moduledata.zint.generate(code,data,suffix,options)
+ if not data or data == "" then
+ data = "unset"
+ end
+ local code = cleancode(code)
+ local base = format("zint-%s-%s",code,md5.hex(data))
+ local name = file.addsuffix(base,suffix or "eps")
+ if not lfs.isfile(name) then
+ local temp = file.addsuffix(base,"tmp")
+ local code = numberofcode(code)
+ logs.simple("using 'zint' to generate '%s'",base)
+ io.savedata(temp,data)
+ os.execute(format('%s --barcode=%s --output="%s" --input="%s" %s',zint,code,name,temp,options or ""))
+ os.remove(temp)
+ end
+ return name
+end
+
+\stopluacode
+
+\unprotect
+
+\unexpanded\def\barcode[#1]% [alternative=,text=]
+ {\bgroup
+ \getdummyparameters
+ [\c!alternative=,\c!text=,#1]%
+ \externalfigure
+ [\cldcontext{moduledata.zint.generate("\dummyparameter\c!alternative",\!!bs\dummyparameter\c!text\!!es)}]%
+ [#1,\c!alternative=,\c!text=]%
+ \egroup}
+
+\protect
+
+\continueifinputfile{m-zint.mkiv}
+
+\starttext
+
+ \externalfigure[\cldcontext{moduledata.zint.generate("PDF417",[[Hans Hagen]])}]
+ \blank
+ \externalfigure[\cldcontext{moduledata.zint.generate("PDF417","Ton Otten")}]
+ \blank
+ \externalfigure[\cldcontext{moduledata.zint.generate("ISBN","9789490688011")}]
+ \blank
+ \barcode[text=Does It Work?,width=\textwidth]
+ \blank
+ \barcode[alternative=isbn,text=9789490688011,width=3cm]
+
+\stoptext
+
+
diff --git a/tex/context/modules/mkiv/ppchtex.mkiv b/tex/context/modules/mkiv/ppchtex.mkiv
new file mode 100644
index 000000000..d1167d414
--- /dev/null
+++ b/tex/context/modules/mkiv/ppchtex.mkiv
@@ -0,0 +1,3445 @@
+%D \module
+%D [ file=ppchtex,
+%D version=1997.03.19,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=\PPCHTEX\ (Plain Pictex Context cHemie \TEX),
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D suggestions={Tobias Burnus, Dirk Kuypers \& Ton Otten},
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\endinput
+
+% option=test => boxes
+% dummy => file
+% final => file / local run
+%
+% constante van phantom in definitie ONE: \setchemicaltextwidth 300
+%
+% it would be interesting to rewrite this module with todays
+% experiences and new context functionality, maybe ...
+
+% Deze module ondersteunt het zetten van chemische
+% (structuur)formules. Hoewel de macro' zijn afgestemd op
+% CONTEXT, zijn ze ook buiten deze zetomgeving te gebruiken.
+%
+% Dit is, afgezien van updates, de definitieve versie van
+% PPCHTEX. Gebruikersgemak, eenvoud, flexibiliteit, en
+% snelheid zijn inmiddels redelijk geoptimaliseerd. Dit neemt
+% niet weg dat hier en daar nog verbetering mogelijk is. Dit
+% zal dan ook nog gebeuren.
+%
+% Volgende versies zullen tenminste dezelfde functionaliteit
+% hebben. We houden ons natuurlijk het recht voor de kwaliteit
+% van de output te verbeteren. Daarnaast staan nog op het
+% wensenlijstje:
+%
+% - optimaliseren in termen van proces-tijd
+% - aanpassen naamgeving van interne macro's
+% - toevoegen van functionaliteit
+% - in \x!-vorm omzetten van GIVES, TB enz.
+%
+% De mix tussen engels en nederlands lijkt soms verwarrend.
+% Meestal zijn verborgen macro's engels en zichtbare macro's
+% nederlands. Het gebruik van [ ] en { } sluit aan op andere
+% Context-macro's. Hetzelfde geldt voor instellingen en
+% \start-\stop-constructies.
+%
+% De schijnbaar overbodige \bgroup-\egroup constructie
+% garandeert aansluiting bij de Context-macro's voor het
+% plaatsen van figuren, tabellen en andere floats.
+%
+% Binnen Context worden de macro's geladen met
+% \gebruikextras[chemie]. Daarbij wordt een passende melding
+% getoont. Buiten Context genereren we een melding:
+
+\doifundefined{usemodule}
+ {\writestatus{loading}{ConTeXt Chemical Macro's / 1996.3.1}}
+
+% Er kan gebruik worden gemaakt van PiCTeX of PStricks. Een
+% van deze pakketten moet van te voren zijn geladen.
+%
+% \input prepictex.tex (i.g.v. LaTeX)
+% \input pictex.tex
+% \input postpictex.tex (i.g.v. LaTeX)
+%
+% of:
+%
+% \input multido.tex
+% \input pstricks.tex
+% \input pst-plot.tex
+%
+% In \CONTEXT\ kan men de modules m-pictex en m-pstricks
+% gebruiken. De eerste module laad of efficiente wijze PiCTeX
+% en de tweede module koppelt het PSTRICKS kleurmechanisme
+% aan dat van \CONTEXT.
+
+% PSTricks: {-\chemicalangle} instead of {*0}, which produces
+% faulty ps code when \chemicalangle=0
+
+\startcommands dutch english german
+
+ gotochemical: naarchemie gotochemical zurchemie
+ setupchemical: stelchemiein setupchemical stellechemieein
+ startchemical: startchemie startchemical startchemie
+ stopchemical: stopchemie stopchemical stopchemie
+ definechemical: definieerchemie definechemical definierechemie
+ chemical: chemie chemical chemie
+ toptext: boventekst toptext textueber
+ bottext: ondertekst bottext textunter
+ midtext: middentekst midtext textmitte
+
+\stopcommands
+
+\doifundefined{fiverm} % In the more recent LaTeX versions
+ {\font\fiverm=cmr5 } % \fiverm is no longer (pre)defined.
+
+\newconstant\chemicaldrawingmode
+
+\doifelsedefined{beginpicture} % PiCTeX
+ {\doifelsedefined{startMPdrawing}
+ {\chemicaldrawingmode\plustwo } % MetaPost
+ {\chemicaldrawingmode\zerocount}} % raw
+ {\doifelsedefined{psaxes}
+ {\chemicaldrawingmode\plusone } % PSTricks
+ {\chemicaldrawingmode\plusthree}} % unknown
+
+\ifcase\chemicaldrawingmode
+ \writestatus{ppchtex}{using PiCTeX}
+\or
+ \writestatus{ppchtex}{using PSTricks (still experimental)}
+ \writestatus{ppchtex}{automatic sizing not (yet) supported}
+\or
+ \writestatus{ppchtex}{using PiCTeX and MetaPost}
+\else
+ \writestatus{ppchtex}{load PiCTeX (+pre/post) or PSTricks (+pst_plot) first}
+ \bgroup
+ \read16 to \exit
+ \egroup
+ \expandafter\endinput
+\fi
+
+%I n=Chemie
+%I c=\stelchemiein,\chemie
+%I
+%I Chemische formules kunnen worden gezet met behulp van de
+%I onderstaande commando's:
+%I
+%I buiten $ en $$ :
+%I
+%I \chemie[segmenten][symbolen]
+%I
+%I \startchemie[instellingen]
+%I \chemie...
+%I \chemie...
+%I \stopchemie
+%I
+%I en binnen $ en $$:
+%I
+%I \chemie{}{}
+%I
+%I Voor tekst, uitleg en voorbeelde verwijzen we vooralsnog
+%I naar de handleiding.
+%P
+%I Het gedrag van de macro's kan worden ingesteld met:
+%I
+%I \stelchemiein[breedte=,hoogte=,links=,rechts=,boven=,
+%I onder=,korps=,schaal=,status=,assenstelsel=,kader=,
+%I variant=,optie=,formaat=,tekstformaat=,resolutie=,
+%I offset=,letter=]
+%I
+%I Structuren kunnen worden voorgedefinieerd met het commando
+%I
+%I \definieerchemie[naam]{\chemie...}
+
+%S \startsetup
+%S \command
+%S [\!stelchemiein]
+%S \type
+%S [\c!vars!]
+%S \variable
+%S [\c!breedte]
+%S [\c!number!,\v!passend]
+%S [0]
+%S \variable
+%S [\c!hoogte]
+%S [\c!number!,\v!passend]
+%S [0]
+%S \variable
+%S [\c!links]
+%S [\c!number!]
+%S [0]
+%S \variable
+%S [\c!rechts]
+%S [\c!number!]
+%S [0]
+%S \variable
+%S [\c!boven]
+%S [\c!number!]
+%S [0]
+%S \variable
+%S [\c!onder]
+%S [\c!number!]
+%S [0]
+%S \variable
+%S [\c!resolutie]
+%S [\c!number!]
+%S [\outputresolution]
+%S \variable
+%S [\c!korps]
+%S [10pt,11pt,12pt]
+%S [\bodyfontsize]
+%S \variable
+%S [\c!schaal]
+%S [\v!klein,\v!middel,\v!groot]
+%S [\v!middel]
+%S \variable
+%S [\c!formaat]
+%S [\v!klein,\v!middel,\v!groot]
+%S [\v!groot]
+%S \variable
+%S [\c!tekstformaat]
+%S [\v!klein,\v!middel,\v!groot]
+%S [\v!groot]
+%S \variable
+%S [\c!status]
+%S [\v!start,\v!stop]
+%S [\v!start]
+%S \variable
+%S [\c!kader]
+%S [\v!aan,\v!uit]
+%S [\v!uit]
+%S \variable
+%S [\c!assenstelsel]
+%S [\v!aan,\v!uit]
+%S [\v!uit]
+%S \variable
+%S [\c!optie]
+%S [\v!test]
+%S []
+%S \variable
+%S [\c!variant]
+%S [1,2]
+%S [1]
+%S \variable
+%S [\c!offset]
+%S [HIGH,LOW]
+%S [LOW]
+%S \variable
+%S [\c!letter]
+%S [\c!command!]
+%S [\rm]
+%S \stopsetup
+
+%S \startsetup
+%S \command
+%S [\v!startchemie]
+%S \type
+%S [\c!vars!\c!stp!]
+%S \inheritvariable
+%S [\v!stelchemiein]
+%S []
+%S \stopsetup
+
+%S \startsetup
+%S \command
+%S [\v!chemie]
+%S \type
+%S [\c!vals!\c!vals!]
+%S \value
+%S [\c!list!]
+%S []
+%S \value
+%S [\c!list!]
+%S []
+%S \stopsetup
+
+%S \startsetup
+%S \command
+%S [definieerchemie]
+%S \type
+%S [\c!val!\c!arg!]
+%S \value
+%S [\c!text!]
+%S []
+%S \stopsetup
+
+\unprotect
+
+% Om te voorkomen dat sub- en superscripts botsen passen we
+% wat fontdimen's aan (Knuth, The TeXBook, p179). Helaas
+% kunnen deze instellingen niet lokaal worden gehouden door
+% groeperen, vandaar dat een en ander moet worden geset n
+% gereset.
+%
+% Er dient een relatie te worden gelegd met de afmetingen
+% van de letters. In een eerdere versie werden daartoe de
+% \fontdimen's opgehoogd. Omdat dit problemen gaf bij
+% scaled fonts, is bij nader inzien gekozen voor de
+% onderstaande oplossing, waarbij de nieuwe waarden worden
+% afgeleid van de x-height (\fontexheight). De factor 0.70
+% is min of meer experimenteel vastgesteld. Soms worden de
+% regels iets verder uit elkaar gezet. Jammer. Italic fonts
+% hebben grotere cijfers en vallen min of meer uit de boot.
+
+\newif\ifloweredsubscripts % this will be redone in the mkiv ways
+
+\def\setsubscripts
+ {\def\dosetsubscript##1##2##3%
+ {\dimen0=##3\fontexheight##2%
+ \setxvalue{@@\string##1\string##2}{\the##1##2\relax}%
+ ##1##2=\dimen0\relax}%
+ \def\dodosetsubscript##1##2%
+ {\dosetsubscript{##1}{\textfont 2}{##2}%
+ \dosetsubscript{##1}{\scriptfont 2}{##2}%
+ \dosetsubscript{##1}{\scriptscriptfont2}{##2}}%
+ %dodosetsubscript\mathsupnormal {?}%
+ \dodosetsubscript\mathsubnormal {.7}%
+ \dodosetsubscript\mathsubcombined{.7}%
+ \global\loweredsubscriptstrue}
+
+\def\resetsubscripts
+ {\ifloweredsubscripts
+ \def\doresetsubscript##1##2%
+ {\dimen0=\getvalue{@@\string##1\string##2}\relax
+ ##1##2=\dimen0}%
+ \def\dodoresetsubscript##1%
+ {\doresetsubscript{##1}{\textfont2}%
+ \doresetsubscript{##1}{\scriptfont2}%
+ \doresetsubscript{##1}{\scriptscriptfont2}}%
+ %dodoresetsubscript\mathsupnormal
+ \dodoresetsubscript\mathsubnormal
+ \dodoresetsubscript\mathsubcombined
+ \global\loweredsubscriptsfalse
+ \fi}
+
+\ifx\Umathchar\undefined \else
+ % for the moment we nil them, soon we will have a proper
+ % way to deal with this
+ \let\setsubscripts \relax
+ \let\resetsubscripts\relax
+\fi
+
+\def\doresetsubscripts
+ {\resetsubscripts}
+
+\def\sethighsubscripts
+ {\resetsubscripts
+ \let\dosetsubscripts=\relax}
+
+\def\setlowsubscripts
+ {\def\dosetsubscripts{\setsubscripts}}
+
+\setlowsubscripts
+
+\newcount\horchemical % t.z.t. \newcounter
+\newcount\verchemical % t.z.t. \newcounter
+\newcount\txtchemical % t.z.t. \newcounter
+\newcount\levchemical % t.z.t. \newcounter
+
+\newif\ifinchemical \inchemicalfalse
+\newif\iffixedchemical \fixedchemicalfalse
+
+\newbox\chemicalsymbols
+
+% Eigenlijk moeten de constanten en variabelen in cont-nl.tex
+% staan. Dit pakket is echter relatief onafhankelijk van CONTEXT.
+
+\definesystemvariable {chemical}
+
+\definesystemconstant {chemical}
+
+\definesystemconstant {translate}
+\definesystemconstant {distance}
+\definesystemconstant {mirror}
+\definesystemconstant {rotate}
+\definesystemconstant {substitute}
+\definesystemconstant {angle}
+
+\definesystemconstant {executechemical}
+\definesystemconstant {chemicaltextelement}
+\definesystemconstant {chemicallinesegment}
+\definesystemconstant {chemicalcircsegment}
+
+\def\chemicalspace {\quad}
+
+% begin van experiment:
+%
+% De onderstaande twee macro's kunnen worden gebruikt voor
+% bijvoorbeeld een interactiemechanisme.
+%
+% \localgotochemical {verwijzing} {tekst}
+% \localthisischemical {verwijzing}
+
+\def\dowithchemical%
+ {}
+
+\def\localgotochemical#1#2{\gotobox{#2}[#1]}
+\def\localthisischemical#1{\pagereference[#1]}
+
+% eind van experiment
+
+\def\setchemicalmaximum #1
+ {\def\maxchemical{#1}}
+
+\def\doifchemicalnumber#1#2#3%
+ {\doifelsenumber{#1}
+ {\ifnum#1>\maxchemical\relax
+ \writestatus{ppchtex}{number #1 is skipped}%
+ \else
+ #3%
+ \fi}
+ {\unknownchemical{#2}}}%
+
+\newif\ifsmallchemicaltext
+
+\let\@@localchemicalstyle\empty
+
+\unexpanded\def\setupchemicalformat[#1]%
+ {\processaction
+ [\getvalue{#1\c!size}]
+ [ \v!small=>\def\@@localchemicalformat{\scriptscriptstyle},
+ \v!medium=>\def\@@localchemicalformat{\ifsmallchemicaltext\scriptscriptstyle\else\scriptstyle\fi},
+ \v!big=>\def\@@localchemicalformat{\ifsmallchemicaltext\scriptstyle\else\textstyle\fi},
+ \s!unknown=>\def\@@localchemicalformat{\getvalue{#1\c!size}}]%
+ \processaction
+ [\getvalue{#1\c!textsize}]
+ [ \v!small=>\def\@@localchemicalstyle{\scriptscriptstyle},
+ \v!medium=>\def\@@localchemicalstyle{\ifsmallchemicaltext\scriptscriptstyle\else\scriptstyle\fi},
+ \v!big=>\def\@@localchemicalstyle{\ifsmallchemicaltext\scriptstyle\else\textstyle\fi},
+ \s!unknown=>\def\@@localchemicalstyle{\getvalue{#1\c!textsize}}]%
+ \processaction
+ [\getvalue{#1\c!scale}]
+ [ \v!small=>\def\@@localchemicalscale{500},
+ \v!medium=>\def\@@localchemicalscale{625},
+ \v!big=>\def\@@localchemicalscale{750},
+ \s!unknown=>\def\@@localchemicalscale{\getvalue{#1\c!scale}}]}
+
+\def\@@currentchemicalformat
+ {\ifinchemical
+ \@@localchemicalformat
+ \else
+ \@@localchemicalstyle
+ \fi}
+
+\def\dosetupchemical[#1]%
+ {\getparameters[\??chemical\s!chemical][#1]%
+ \doifelse{\@@chemicalchemicaloffset}{LOW}
+ {\setlowsubscripts}
+ {\sethighsubscripts}%
+ \setupchemicalformat[\??chemical\s!chemical]%
+ \ignorespaces}
+
+\unexpanded\def\setupchemical
+ {\dosingleargument\dosetupchemical}
+
+\def\@@dochemicalstyle% % default mapping
+ {\@@chemicalstyle}
+
+\def\@@dochemicalcolor% % no mapping yet
+ {}
+
+\def\@@chemicalstyle % $inner-style$ % (overloaded)
+ {\@@chemicalchemicalstyle} % $$outer-style$$
+
+\def\@@writechemicalstate#1#2%
+ {}
+
+\def\@@beginchemicallocalpicture
+ {\ifcase\chemicaldrawingmode
+ \beginpicture
+ \or
+ \pspicture(0,0)(0,0) % is this permitted ?
+ \or
+ \pushMPdrawing
+ \startMPdrawing
+ %prologues := 1 ;
+ %input mp-tool ;
+ u := 10*\@@chemicalunit;
+ bboxmargin := 0pt ;
+ pickup pencircle scaled 2u ; % ???
+ \stopMPdrawing
+ \beginpicture
+ \fi}
+
+\def\@@endchemicallocalpicture#1#2%
+ {\ifcase\chemicaldrawingmode
+ \endpicture
+ \or
+ \endpspicture
+ \or
+ \resetchemicalcoordinates
+ \setbox2\hbox{\ignoreMPboxdepth\getMPdrawing}%
+ \wd2\zeropoint
+ \ht2\zeropoint
+ \dp2\zeropoint
+ \put {\box2} at 0 0
+ \endpicture
+ \popMPdrawing
+ \fi}
+
+\def\@@beginchemicalpicture#1#2#3#4%
+ {\ifnum\chemicaldrawingmode=1
+ \pspicture(#1,#3)(#2,#4)%
+ \def\account##1##2{}%
+ \psaxes[axesstyle=none,labels=none,ticks=none](#1,#3)(#2,#4)%
+ \else
+ \beginpicture
+ \setplotarea
+ x from {#1} to {#2},
+ y from {#3} to {#4}
+ \iffixedchemical
+ \accountingon
+ \def\account##1##2%
+ {\put {} at {##1} {##2} }%
+ \else
+ \accountingoff
+ \def\account##1##2{}%
+ \fi
+ \fi
+ \ignorespaces}
+
+\def\@@endchemicalpicture%
+ {\ifcase\chemicaldrawingmode
+ \put {\box\chemicalsymbols} at 0 0 % elders
+ \endpicture
+ \or
+ \rput(0,0){\box\chemicalsymbols}%
+ \endpspicture
+ \or
+ \put {\box\chemicalsymbols} at 0 0 % elders
+ \ifMPdrawingdone
+ \resetchemicalcoordinates
+ \setbox2\hbox{\ignoreMPboxdepth\getMPdrawing}%
+ \wd2\zeropoint
+ \ht2\zeropoint
+ \dp2\zeropoint
+ \put {\box2} at 0 0 %
+ \fi
+ \endpicture
+ \fi}
+
+\def\@@setchemicalcoordinatesystem#1%
+ {\edef\@@chemicalunit{#1}%
+ \ifcase\chemicaldrawingmode
+ \setcoordinatesystem units <\@@chemicalunit,\@@chemicalunit> %
+ \or
+ \psset{unit=\@@chemicalunit}%
+ \or
+ \setcoordinatesystem units <\@@chemicalunit,\@@chemicalunit> %
+ \startMPdrawing
+ %input mp-tool ;
+ %prologues := 1 ;
+ u := 10*#1;
+ bboxmargin := 0pt ;
+ pickup pencircle scaled 2u ; % ???
+ \stopMPdrawing
+ \fi}
+
+\ifx\MPdivten\undefined % hack to prevent overflows in mp
+ \def\MPdivten[#1]{\withoutpt\the\dimexpr#1pt/10\relax}
+\fi
+
+\def\@@setchemicalaxis#1#2#3#4%
+ {\ifcase\chemicaldrawingmode
+ \axis
+ bottom shiftedto y=0
+ ticks from {#1} to {#2} by 500 /
+ \axis
+ left shiftedto x=0
+ ticks from {#3} to {#4} by 500 / %
+ \or
+ \psaxes[labels=none,Dx=500,Dy=500](0,0)(#1,#3)(#2,#4)%
+ \or
+ \global\MPdrawingdonetrue
+ % we need to div beforehand because of mp limitations
+ \startMPdrawing
+ x1 := \MPdivten[#1]u ; x2 := \MPdivten[#2]u;
+ y1 := \MPdivten[#3]u ; y2 := \MPdivten[#4]u;
+ draw z1--(x2,y1)--z2--(x1,y2)--cycle ;
+ d := 50u ; dd := 10u ;
+ draw (x1,0)--(x2,0) ;
+ draw (0,y1)--(0,y2) ;
+ for i=d step -d until x1: draw (i,dd)--(i,-dd) ; endfor ;
+ for i=d step d until x2: draw (i,dd)--(i,-dd) ; endfor ;
+ for i=d step -d until y1: draw (-dd,i)--(dd,i) ; endfor ;
+ for i=d step d until y2: draw (-dd,i)--(dd,i) ; endfor ;
+ \stopMPdrawing
+ \fi}
+
+\def\@@setsecondchemicalplotsymbol%
+ {\ifcase\chemicaldrawingmode
+ \!!widtha=50.8mm
+ \divide\!!widtha by \@@chemicalresolution\relax
+ \plotsymbolspacing=\!!widtha
+ \setplotsymbol({\vrule\s!height\!!widtha\s!width\!!widtha})%
+ \fi}
+
+% Something for Dirk:
+
+\newcount \currentchemical
+
+%\newif \ifskipchemical
+
+\def\setchemicaldimensions#1#2#3%
+ {\bgroup
+ \global\advance\currentchemical by 1
+ \dimen0=#1\relax
+ \dimen2=#2\relax
+ \dimen4=#3\relax
+ \setxvalue{chemical::\the\currentchemical}%
+ {\noexpand\docommand{\the\dimen0}{\the\dimen2}{\the\dimen4}}%
+ \egroup}
+
+\ifx\normalchemicalframe\undefined
+ \let\normalchemicalframe\hbox % hook for educational purposes
+\fi
+
+\unexpanded\def\complexstartchemical[#1]%
+ {\copyparameters
+ [\??chemical][\??chemical\s!chemical]
+ [\c!width,\c!height,\c!left,\c!right,\c!top,\c!bottom,
+ \c!bodyfont,\c!size,\c!scale,\c!state,\c!frame,\c!axis,\c!factor,
+ \c!location,\c!option,\c!alternative,\c!resolution,\c!offset,\c!style,
+ \c!color,\c!rulecolor,\c!rulethickness]%
+ \getparameters
+ [\??chemical]
+ [#1]%
+ %
+ \setupchemicalformat[\??chemical]%
+ %
+ \ifnum\chemicaldrawingmode=2
+ \resetMPdrawing
+ \fi
+ %
+ \doif{\@@chemicalalternative}{2}
+ {\@@setsecondchemicalplotsymbol}%
+ %
+ \doif{\@@chemicalaxis}\v!on
+ {\let\chemicalframe\hbox}%
+ %
+ \!!counta=250000
+ \divide\!!counta by \@@localchemicalscale
+ \!!widtha=\@@chemicalbodyfont
+ \divide\!!widtha by \!!counta
+ \@@setchemicalcoordinatesystem{\the\!!widtha}%
+ %
+ % \!!counta = -x \!!countc = -y
+ % \!!countb = +x \!!countd = +y
+ %
+ \def\calculateaxis##1##2##3##4##5%
+ {##1=##3\relax
+ ##2=##4\relax
+ \ifnum##5=0
+ \ifnum##3=0
+ \ifnum##4=0
+ ##1=2000
+ ##2=2000
+ \fi
+ \fi
+ \else
+ \ifnum##3=0
+ \ifnum##4=0
+ ##1=##5\relax
+ \divide##1 by 2
+ ##2=##1\relax
+ \else
+ ##1=##5\relax
+ \advance##1 by -##2\relax
+ \fi
+ \else
+ \ifnum##4=0
+ ##2=##5\relax
+ \advance##2 by -##1\relax
+ \fi
+ \fi
+ \fi}%
+ \fixedchemicalfalse
+ \doif\@@chemicalwidth\v!fit
+ {\edef\@@chemicalwidth
+ {\ifnum\chemicaldrawingmode=1 2000 \else 1 \fi}%
+ \fixedchemicaltrue}%
+ \doif\@@chemicalheight\v!fit
+ {\edef\@@chemicalheight
+ {\ifnum\chemicaldrawingmode=1 2000 \else 1 \fi}%
+ \fixedchemicaltrue}%
+ \doifelse\@@chemicallocation\v!intext
+ {\!!counta=0 \!!countb=0
+ \!!counta=0 \!!countd=0 }
+ {\calculateaxis
+ \!!counta\!!countb
+ \@@chemicalleft\@@chemicalright\@@chemicalwidth
+ \calculateaxis
+ \!!countc\!!countd
+ \@@chemicalbottom\@@chemicaltop\@@chemicalheight}%
+ %
+ \edef\@@chemheight {\the\!!countc}%
+ \edef\@@chemdepth {\the\!!countd}%
+ \edef\@@chemicaltop {\the\!!countc}%
+ \edef\@@chemicalbottom{\the\!!countd}%
+ %
+ \doifelseinset\v!on{\@@chemicalframe,\@@chemicalaxis}
+ {\def\@@chemicalborder{\chemicalframe}}
+ {\def\@@chemicalborder{\normalchemicalframe}}%
+ %
+ \setbox0=\hbox\bgroup % this was a \vbox which took \hsize
+ %
+ \@@beginchemicalpicture
+ {-\the\!!counta}{\the\!!countb}
+ {-\the\!!countc}{\the\!!countd}%
+ \doif{\@@chemicalstate}\v!start
+ {\doif\@@chemicalaxis\v!on
+ {\@@setchemicalaxis
+ {-\the\!!counta}{\the\!!countb}
+ {-\the\!!countc}{\the\!!countd}}}%
+ \doifelse\@@chemicaloption\v!test
+ {\def\@@writechemicalstate##1##2%
+ {\convertargument##2\to\ascii
+ \writestatus{##1}{\ascii}}}
+ {\def\@@writechemicalstate##1##2{}}%
+ \ignorespaces}
+
+\def\dostartchemical%
+ {\catcode`\^=\superscriptcatcode% t.b.v. \enableduplication
+ \catcode`\_=\subscriptcatcode % t.b.v. de zekerheid
+ \begingroup
+ \inchemicaltrue
+ \def\toptext##1{\gdef\thetoptext{##1}\ignorespaces}\toptext{}%
+ \def\bottext##1{\gdef\thebottext{##1}\ignorespaces}\bottext{}%
+ \def\midtext##1{\gdef\themidtext{##1}\ignorespaces}\midtext{}%
+ \def\@@chemicalpostponed{}%
+ \complexorsimpleempty\startchemical}
+
+\unexpanded\def\startchemical
+ {\bgroup % t.b.v. ungrouped floats
+ \dostartchemical}
+
+\unexpanded\def\stopchemical
+ {\checkchemicalpicture
+ \@@endchemicalpicture
+ \egroup
+ \ifnum\chemicaldrawingmode=1
+ \dimen0=\@@chemicalunit
+ \setbox0=\hbox{\lower\@@chemdepth\dimen0\box0}%
+ \ht0=\@@chemheight\dimen0
+ \dp0=\@@chemdepth\dimen0
+ \fi
+ \dimen0=\ht0
+ \advance\dimen0 by \dp0
+ \inchemicalfalse % enables \chemie{} in text
+ \setbox4=\alignedchemical\themidtext
+ \setbox6=\alignedchemical\thetoptext
+ \setbox8=\alignedchemical\thebottext
+ \setbox4=\hbox to \wd0
+ {\strut\hss$\vcenter{\box4}$\hss}%
+ \setbox2=\vbox to \dimen0
+ {\hbox to \wd0{\strut\hss\box6\hss}
+ \vfill
+ \hbox to \wd0{\strut\hss\box8\hss}
+ \vss}% disables the depth
+ \wd0=0pt \wd4=0pt
+ \ht2=\ht0 \dp2=\dp0
+ \ht4=\ht0 \dp4=\dp0
+ \@@chemicalborder{\box0\box4\box2}% text on top of chemicals
+ \endgroup
+ \ignorespaces
+ \egroup} % t.b.v. ungrouped floats
+
+\def\alignedchemical#1%
+ {\vtop
+ {\def\par{\egroup\hbox\bgroup\strut}%
+ \let\\=\par
+ \let\endgraf=\par
+ \hbox\bgroup\strut#1\egroup}}
+
+% \setchemicalcoordinates{#1}{#2}
+%
+% #1: verplaatsing in x-richting
+% #2: verplaatsing in y-richting
+
+\newif\ifchemicaldirection
+
+\def\checkchemicaldirection#1#2%
+ {\ifchemicaldirection
+ \ifnum#1>0 \advance\horchemical -\chemicaldirection \fi
+ \ifnum#1<0 \advance\horchemical +\chemicaldirection \fi
+ \ifnum#2>0 \advance\verchemical -\chemicaldirection \fi
+ \ifnum#2<0 \advance\verchemical +\chemicaldirection \fi
+ \chemicaldirectionfalse
+ \fi}
+
+\def\processchemicaldirection%
+ {\chemicaldirectiontrue\processchemicaltranslate}
+
+\def\setchemicalcoordinates#1#2%
+ {\advance\horchemical #1\relax
+ \advance\verchemical #2\relax
+ \checkchemicaldirection{#1}{#2}%
+ \!!counta=-\horchemical\edef\chemicalxoffset{\the\!!counta}%
+ \!!countb=-\verchemical\edef\chemicalyoffset{\the\!!countb}%
+ \ifnum\chemicaldrawingmode=1
+ % njet
+ \else
+ \setcoordinatesystem point at {\the\horchemical} {\the\verchemical}
+ \fi}
+
+\def\resetchemicalcoordinates
+ {\horchemical=0
+ \verchemical=0
+ \edef\chemicalxoffset{0}%
+ \edef\chemicalyoffset{0}%
+ \ifnum\chemicaldrawingmode=1
+ % njet
+ \else
+ \setcoordinatesystem point at 0 0
+ \fi}
+
+\def\restorechemicalcoordinates
+ {%\writestatus{ppchtex}{restoring \the\horchemical,\the\verchemical}%
+ \edef\chemicalxoffset{\the\horchemical}%
+ \edef\chemicalyoffset{\the\verchemical}%
+ \ifnum\chemicaldrawingmode=1
+ % njet
+ \else
+ \setcoordinatesystem point at {\the\horchemical} {\the\verchemical}
+ \fi}
+
+\def\setchemicaltranslate #1 #2 #3
+ {\setvalue{\s!translate#1}{\setchemicalcoordinates{#2}{#3}}}
+
+\def\processchemicaltranslate#1%
+ {\def\doprocess[##1##2]%
+ {\doifchemicalnumber{##1}{MOV#1}
+ {\ifnum##1=0
+ \def\chemicaloffset{0}% incompatible change
+ \resetchemicalcoordinates
+ \else
+ \getvalue{\s!translate##1}%
+ \dochemicaloffset{##1}%
+ \def\chemicaloffset{0}%
+ \fi}}%
+ \doprocess[#1]}
+
+\def\setchemicaldistance #1
+ {\setvalue{\s!distance1}{\setchemicalcoordinates{-#1}{ 0}}%
+ \setvalue{\s!distance2}{\setchemicalcoordinates{ 0}{ #1}}%
+ \setvalue{\s!distance3}{\setchemicalcoordinates{ #1}{ 0}}%
+ \setvalue{\s!distance4}{\setchemicalcoordinates{ 0}{-#1}}}
+
+\def\setchemicaldirection #1
+ {\def\chemicaldirection{#1}}
+
+\def\processchemicaldistance#1%
+ {\def\doprocess[##1##2]%
+ {\doifchemicalnumber{##1}{ADJ#1}
+ {\ifnum##1=0
+ \resetchemicalcoordinates
+ \else
+ \def\@@chemicalpostponed{\getvalue{\s!distance##1}}%
+ \@@chemicalpostponed
+ \fi}}%
+ \doprocess[#1]}
+
+\def\setchemicalsubstitute #1
+ {\setvalue{\s!substitute1}{\setchemicalcoordinates{-#1}{ 0}}%
+ \setvalue{\s!substitute2}{\setchemicalcoordinates{ 0}{ #1}}%
+ \setvalue{\s!substitute3}{\setchemicalcoordinates{ #1}{ 0}}%
+ \setvalue{\s!substitute4}{\setchemicalcoordinates{ 0}{-#1}}}
+
+\def\processchemicalsubstitute#1%
+ {\def\doprocess[##1##2]%
+ {\doifchemicalnumber{##1}{SUB#1}
+ {\ifnum##1=0
+ \resetchemicalcoordinates
+ \else
+ \def\@@chemicalpostponed{\getvalue{\s!substitute##1}}%
+ \@@chemicalpostponed
+ \fi}}%
+ \doprocess[#1]}
+
+% Het is mogelijk een offset of move meerdere malen uit te
+% voeren, door een nummer voor het commando te plaatsen.
+
+\def\chemicalrepeat {1}
+
+\def\redoprocesschemical[#1#2]%
+ {\doifelseinstring{#1}{0123456789.}
+ {\edef\chemicalrepeat{\chemicalrepeat#1}%
+ \redoprocesschemical[#2]}
+ {\processchemical[#1#2]%
+ \def\chemicalrepeat{1}}}
+
+\def\doprocesschemical[#1#2]#3%
+ {\doifelseinstring{#1}{0123456789.}
+ {\def\chemicalrepeat{#1}%
+ \redoprocesschemical[#2]}
+ {#3}}
+
+% \dochemicaloffset{#1}
+%
+% #1: binding
+
+\def\chemicaloffset{0}
+
+\def\processchemicaloffset#1%
+ {\dimen0=62500 sp % real calc on cardinals, funny number
+ \dimen0=\chemicalrepeat\dimen0
+ \divide\dimen0 by \@@localchemicalscale
+ \!!counta=\dimen0
+ \def\doprocess[##1##2]%
+ {\doifelseinstring{##1}{128}
+ {\edef\chemicaloffset{\the\!!counta}}
+ {\doifelseinstring{##1}{456}
+ {\edef\chemicaloffset{-\the\!!counta}}
+ {\doifelse{##1}{0}
+ {\edef\chemicaloffset{0}}
+ {\unknownchemical{OFF#1}}}}}%
+ \doprocess[#1]}
+
+\def\dochemicaloffset#1%
+ {\ifnum\chemicaloffset=0
+ \def\undochemicaloffset{}%
+ \else
+ \setchemicalcoordinates{-\chemicaloffset}{0}%
+ \def\undochemicaloffset%
+ {\setchemicalcoordinates{\chemicaloffset}{0}%
+ \def\undochemicaloffset{}}%
+ \fi}
+
+\def\processchemicalphantom#1#2%
+ {\setbox0=\hbox
+ {\def\splitoff##1????{##1}%
+ $\@@dochemicalstyle{\@@localchemicalformat\splitoff#2}$}%
+ \dimen0=.25\wd0
+ \divide\dimen0 by \@@localchemicalscale
+ \!!counta=\dimen0
+ \doifelseinstring{#1}{128}
+ {\edef\chemicaloffset{\the\!!counta}}
+ {\doifelseinstring{#1}{456}
+ {\edef\chemicaloffset{-\the\!!counta}}
+ {\doifelse{#1}{0}
+ {\edef\chemicaloffset{0}}
+ {\unknownchemical{OF#1:#2}}}}}
+
+% \dosetchemicalrotation{#1}{#2}
+%
+% #1: cos(phi)
+% #2: sin(phi)
+
+\def\chemicalrotation {1}
+\def\chemicalangle {0}
+\def\chemicalxoffset {0}
+\def\chemicalyoffset {0}
+
+\def\setchemicalmirror#1%
+ {\setvalue{\s!mirror#1}{*}}
+
+\def\resetchemicalmirror#1%
+ {\resetvalue{\s!mirror#1}}
+
+\def\togglechemicalmirror#1%
+ {\doifelse{\getvalue{\s!mirror#1}}{*}
+ {\resetchemicalmirror{#1}}
+ {\setchemicalmirror{#1}}}
+
+\def\setchemicalrotation #1 #2 #3 #4 #5 #6 #7 #8 #9
+ {\setvalue{\s!rotate1.#1}{\dosetchemicalrotation{#2}{#3}}%
+ \setvalue{\s!rotate2.#1}{\dosetchemicalrotation{#4}{#5}}%
+ \setvalue{\s!rotate3.#1}{\dosetchemicalrotation{#6}{#7}}%
+ \setvalue{\s!rotate4.#1}{\dosetchemicalrotation{#8}{#9}}}
+
+\def\setchemicalangle #1 #2 #3 #4 #5
+ {\setvalue{\s!angle1.#1}{\dosetchemicalangle{#2}}%
+ \setvalue{\s!angle2.#1}{\dosetchemicalangle{#3}}%
+ \setvalue{\s!angle3.#1}{\dosetchemicalangle{#4}}%
+ \setvalue{\s!angle4.#1}{\dosetchemicalangle{#5}}}
+
+\def\chemicalrotate[#1]%
+ {\doifelsedefined{\s!mirror#1}
+ {\getvalue{\s!rotate\chemicalrotation.#1\getvalue{\s!mirror#1}}%
+ \getvalue{\s!angle\chemicalrotation.#1\getvalue{\s!mirror#1}}}
+ {\getvalue{\s!rotate\chemicalrotation.#1}%
+ \getvalue{\s!angle\chemicalrotation.#1}}}
+
+\def\dosetchemicalangle#1% zwak zie onder
+ {\def\chemicalangle{#1}}
+
+\def\dosetchemicalrotation#1#2%
+ {\ifnum\chemicaldrawingmode=1
+ % njet
+ \else
+ \startrotation by {#1} {#2} %% \stoprotation (t.b.v. testen)
+ \fi}
+
+\def\doresetchemicalrotation
+ {\ifnum\chemicaldrawingmode=1
+ % njet
+ \else
+ \stoprotation
+ \fi}
+
+\def\processchemicalrotation#1%
+ {\def\doprocess[##1##2]%
+ {\doifelsenumber{##1}
+ {\def\chemicalrotation{##1}}
+ {\unknownchemical{ROT#1}}}%
+ \doprocess[#1]}
+
+% \filtertextelement[#1][#2][#3][#4]
+%
+% #1: volgnummer
+% #2: offset in uitlijningen
+% #3: lijst met uitlijningen -> \chemicalloca
+% #4: lijst met teksten -> \chemicaltext
+
+\def\setchemicallocation#1%
+ {\doifelse{#1}{}
+ {\edef\chemicalloca{c}}
+ {\edef\chemicalloca{#1}}}
+
+\newif\iffixedchemicaltext
+
+\def\filterchemicaltextelement[#1][#2][#3][#4]%
+ {\ifchemicaltextconstant
+ \def\chemicaltext{#4}%
+ \setchemicallocation{}%
+ \else
+ \ifnum#1=0\relax
+ \setchemicallocation{}%
+ \else
+ \iffixedchemicaltext
+ \!!counta#2
+ \else
+ \!!counta=\chemicalrotation
+ \advance\!!counta -1
+ \multiply\!!counta #2
+ \advance\!!counta #1
+ \fi
+ \getfromcommalist[#3][\the\!!counta]%
+ \setchemicallocation\commalistelement
+ \fi
+ \ifchemicalpicture
+ \let\chemicaltext\relax
+ \else
+ \advance\txtchemical 1
+ \getfromcommalist[#4][\txtchemical]%
+ \let\chemicaltext\commalistelement
+ \fi
+ \fi
+ \fixedchemicaltextfalse}
+
+% \putchemicaltext{#1}{#2}
+%
+% #1 : x-coordinaat
+% #2 : y-coordinaat
+%
+% \chemicaltext en \chemicalloca worden met \gettextelement
+% opgehaald uit de tweede set bij \chemie
+%
+% Ten behoeve van testdoeleinden wordt gebruik gemaakt van
+% \chemicalframe in plaats van het meer sjieke, maar tevens
+% meer trage \framed.
+
+\ifx\ruledhbox\undefined
+ \def\chemicalframe#1%
+ {\hbox
+ {\vrule\hskip-.4pt
+ \vbox{\hrule\vskip-.4pt\hbox{#1}\vskip-.4pt\hrule}%
+ \hskip-.4pt\vrule}}
+\else
+ \def\chemicalframe#1%
+ {\ruledhbox{#1}}
+\fi
+
+\def\doputchemicaltext#1 [#2] at #3 #4 %
+ {\ifnum\chemicaldrawingmode=1
+ \rput
+ {-\chemicalangle}(\chemicalxoffset,\chemicalyoffset)%
+ {\expanded{\rput[#2]{\chemicalangle}(#3,#4){#1}}}%
+ \else
+ \put {#1} [#2] at {#3} {#4} %
+ \fi}
+
+\def\dodoifsinglelocation#1#2\\#3%
+ {\ifx#2\relax#3\fi}
+
+\def\doifelsesinglelocation#1%
+ {\expandafter\dodoifsinglelocationelse#1\relax\\}
+
+\let\doifsinglelocationelse\doifelsesinglelocation
+
+\def\putchemicaltext#1#2%
+ {\enablechemicalspecials
+ \ifchemicalpicture
+ \setchemicalpicture{#1}{#2}%
+ \else
+ \doifelse\@@chemicaloption\v!test
+ {\def\@@chemicalframe{\chemicalframe}}
+ {\def\@@chemicalframe{}}%
+ \dosetsubscripts
+ \setbox2=\hbox{\@@dochemicalcolor
+ $\@@dochemicalstyle{\@@localchemicalformat \chemicaltext}$}%
+ \setbox4=\hbox{$\@@dochemicalstyle{\@@localchemicalformat C\normalsubscript2\normapsuperscript2}$}%
+ \setbox6=\hbox{$\@@dochemicalstyle{\@@localchemicalformat O}$}% or C
+ \doresetsubscripts
+ \doifnot\@@chemicallocation\v!intext
+ {\ht2=\ht4
+ \dp2=\dp4}%
+ \setbox2=\hbox{\@@chemicalframe{\box2}}%
+ \ifdim\wd2>\wd6
+ \doifelse{#1}{0}
+ {\doifnot{#2}{0}{\wd2=\wd6}}
+ {%\doifsinglelocation\chemicalloca
+ {\doifinset{\chemicalloca}{t,b}{\wd2=\wd6}}}% common ?
+ \fi
+ \expanded
+ {\doputchemicaltext
+ {\noexpand\dowithchemical{\copy2}} % per se \copy2 i.p.v. \box2
+ [\chemicalloca] at {#1} {#2} }
+ \nomoreaccounting
+ \fi
+ \disablechemicalspecials}
+
+\def\setchemicaltextelement #1 #2 #3
+ {\setvalue{\s!chemicaltextelement#1}{\putchemicaltext{#2}{#3}}}
+
+\def\getchemicalfixedtextelement%
+ {\fixedchemicaltexttrue
+ \getchemicaltextelement}
+
+\def\getchemicaltextelement[#1][#2][#3][#4][#5]%
+ {\filterchemicaltextelement[#2][#3][#4][#5]%
+ \doifelse{#2}{0}
+ {\dochemicaloffset{#2}% % incompatible change
+ \putchemicaltext{0}{0}%
+ \undochemicaloffset} % incompatible change
+ {\chemicalrotate[#2]%
+ \dochemicaloffset{#2}%
+ \def\chemicaltextelementnumber{#2}%
+ \getvalue{\s!chemicaltextelement#1}%
+ \getvalue{\s!chemicaltextelement#11}%
+ \getvalue{\s!chemicaltextelement#12}%
+ \getvalue{\s!chemicaltextelement#13}%
+ \undochemicaloffset}}
+
+\def\processchemicaltextelement#1#2#3#4#5%
+ {\def\doprocess[##1##2##3##4##5]%
+ {\doifelse{##1}{?}
+ {\doprocess[1..\maxchemical ????]}
+ {\doifchemicalnumber{##1}{#1#2}
+ {\doifelse{##2##3}{..}
+ {\doifchemicalnumber{##4}{#1#2}
+ {\getchemicaltextelement[#1][##1][#4][#5][#3]%
+ \doifnot{##1}{##4}
+ {\!!counta=##1\relax
+ \advance\!!counta by 1
+ \edef\nextsegment{\the\!!counta}%
+ \doprocess[\nextsegment..##4##5]}}}
+ {\getchemicaltextelement[#1][##1][#4][#5][#3]%
+ \doifnot{##2}{?}{\doprocess[##2##3##4##5]}}}}}%
+ \doprocess[#2]%
+ \smallchemicaltextfalse}
+
+\def\processchemicalsmalltextelement%
+ {\smallchemicaltexttrue\processchemicaltextelement}
+
+\def\processchemicalsmalltextconstant%
+ {\smallchemicaltexttrue\processchemicaltextconstant}
+
+\def\processchemicalunrotatedtextelement#1#2#3#4#5#6%
+ {\bgroup
+ \xdef\@@xxx{0}%
+ \xdef\@@yyy{0}%
+ \def\putchemicaltext##1##2%
+ {\xdef\@@xxx{##1}%
+ \xdef\@@yyy{##2}}%
+ \getvalue{\s!chemicaltextelement#1}%
+ \egroup
+ \bgroup
+ \def\doputchemicaltext##1 [##2] at ##3 ##4 %
+ {\ifnum\chemicaldrawingmode=1
+ \rput
+ {-\chemicalangle}(\chemicalxoffset,\chemicalyoffset)%
+ {\rput{\chemicalangle}(\@@xxx,\@@yyy){\expanded{\rput[##2](##3,##4){##1}}}}%
+ \else
+ \put
+ {\stoprotation \setcoordinatesystem point at 0 0
+ \expanded{\put {##1} [##2] at {##3} {##4} }}
+ at {\@@xxx} {\@@yyy}
+ \fi}%
+ \processchemicaltextelement{#2}{#3}{#4}{#5}{#6}%
+ \egroup}
+
+\newif\ifchemicaltextconstant
+
+\def\processchemicaltextconstant#1#2#3#4%
+ {\chemicaltextconstanttrue
+ \let\@@oldchemicalframe\@@chemicalframe
+ \let\@@chemicalframe\relax
+ \processchemicaltextelement{#1}{#2}{#3}{#4}{}%
+ \let\@@chemicalframe\@@oldchemicalframe
+ \chemicaltextconstantfalse}
+
+% \plotchemicalline{#1}{#2}{#3}{#4}
+%
+% #1: x-coordinaat beginpunt
+% #2: y-coordinaat beginpunt
+% #3: x-coordinaat eindpunt
+% #4: y-coordinaat eindpunt
+
+\newconstant\chemicallinetype
+
+\def\doplotchemicalline
+ {\!!counte=\!!countc \advance\!!counte by -\!!counta
+ \!!countf=\!!countd \advance\!!countf by -\!!countb
+ \bgroup
+ \ifcase\chemicaldrawingmode
+ \ifcase\chemicallinetype
+ % 0 : normal line
+ \plot {\!!counta} {\!!countb} {\!!countc} {\!!countd} /%
+ \or
+ % 1 : normal arrow
+ \arrow <5pt> [.2,.67] from {\!!counta} {\!!countb} to {\!!countc} {\!!countd}
+ \or
+ % 2 : reverse arrow
+ \arrow <5pt> [.2,.67] from {\!!countc} {\!!countd} to {\!!counta} {\!!countb}
+ \or
+ % 3 : unrotated line
+ \put {\stoprotation \setcoordinatesystem point at 0 0
+ \plot 0 0 {\!!counte} {\!!countf} /}
+ [\chemicallineposition] at {\!!counta} {\!!countb}
+ \else
+ % 4 : dashed line
+ \findlength {\plot {\!!counta} {\!!countb} {\!!countc} {\!!countd} /}%
+ \setdashesnear <2pt> for <\totalarclength>%
+ \plot {\!!counta} {\!!countb} {\!!countc} {\!!countd} /%
+ \fi
+ \or
+ \ifcase\chemicallinetype
+ \rput
+ {-\chemicalangle}(\chemicalxoffset,\chemicalyoffset)%
+ {\psline(\!!counta,\!!countb)(\!!countc,\!!countd)}%
+ \or
+ \rput
+ {-\chemicalangle}(\chemicalxoffset,\chemicalyoffset)%
+ {\psline{->}(\!!counta,\!!countb)(\!!countc,\!!countd)}%
+ \or
+ \rput
+ {-\chemicalangle}(\chemicalxoffset,\chemicalyoffset)%
+ {\psline{<-}(\!!counta,\!!countb)(\!!countc,\!!countd)}%
+ \or
+ \rput
+ {-\chemicalangle}(\chemicalxoffset,\chemicalyoffset)%
+ {\expanded{\rput[\chemicallineposition]{-\chemicalangle}%
+ (\!!counta,\!!countb){\psline(0,0)(\!!counte,\!!countf)}}}%
+ \else
+ \psset{linestyle=dashed}%
+ \rput
+ {-\chemicalangle}(\chemicalxoffset,\chemicalyoffset)%
+ {\psline(\!!counta,\!!countb)(\!!countc,\!!countd)}%
+ \fi
+ \or
+ \global\MPdrawingdonetrue
+ \setchemicalattributes
+ \startMPdrawing
+ x0 := \MPdivten[\chemicalxoffset]u ;
+ y0 := \MPdivten[\chemicalyoffset]u ;
+ x1 := \MPdivten[\the\!!counta]u ;
+ y1 := \MPdivten[\the\!!countb]u ;
+ x2 := \MPdivten[\the\!!countc]u ;
+ y2 := \MPdivten[\the\!!countd]u ;
+ x3 := \MPdivten[\the\!!counte]u ;
+ y3 := \MPdivten[\the\!!countf]u ;
+ \ifcase\chemicallinetype
+ % 0 : normal line
+ draw ((z1--z2) rotatedaround(origin,-\chemicalangle))
+ shifted z0 ;
+ \or
+ % 1 : normal arrow
+ drawarrow ((z1--z2) rotatedaround(origin,-\chemicalangle))
+ shifted z0 ;
+ \or
+ % 2 : reverse arrow
+ drawarrow ((z2--z1) rotatedaround(origin,-\chemicalangle))
+ shifted z0 ;
+ \or
+ % 3 : unrotated line % nog \chemicalineposition: t/b
+ draw (origin--z3)
+ shifted (z1 rotatedaround(origin,-\chemicalangle))
+ shifted z0 ;
+ \else
+ % 4 : dashed line
+ draw ((z1--z2) rotatedaround(origin,-\chemicalangle))
+ shifted z0 dashed dashpattern(on 5.5u off 6u) ;
+ \fi
+ \stopMPdrawing
+ \fi
+ \egroup
+ \account\!!counta\!!countb
+ \account\!!countc\!!countd}
+
+\def\plotchemicalline#1#2#3#4%
+ {\!!counta=#1\!!countb=#2\!!countc=#3\!!countd=#4\relax
+ \doplotchemicalline}
+
+\def\plotchemicalfactorline#1#2#3#4%
+ {\!!counta=#1\!!countb=#2\!!countc=#3\!!countd=#4\relax
+ \ifdim\@@chemicalfactor\onepoint=\onepoint \else
+ \scratchdimen\!!counta\s!sp \multiply\scratchdimen1000 \scratchdimen\@@chemicalfactor\scratchdimen \divide\scratchdimen1000 \!!counta\scratchdimen
+ \scratchdimen\!!countc\s!sp \multiply\scratchdimen1000 \scratchdimen\@@chemicalfactor\scratchdimen \divide\scratchdimen1000 \!!countc\scratchdimen
+ \fi
+ \doplotchemicalline}
+
+\def\plotchemicalzline#1#2#3#4%
+ {\!!counta=#1\!!countb=#2\!!countc=#3\!!countd=#4\relax
+ \ifnum\chemicaldrawingmode=2
+ \global\MPdrawingdonetrue
+ \setchemicalattributes
+ \startMPdrawing
+ x0 := \MPdivten[\chemicalxoffset]u ;
+ y0 := \MPdivten[\chemicalyoffset]u ;
+ x1 := \MPdivten[\the\!!counta]u ;
+ y1 := \MPdivten[\the\!!countb]u ;
+ x2 := \MPdivten[\the\!!countc]u ;
+ y2 := \MPdivten[\the\!!countd]u ;
+ filldraw ((
+ \ifnum\chemicalangle>180
+ z1--z2
+ \else\ifnum\chemicalangle<90
+ z1--(z2 shifted (-2u,+2u))--(z2 shifted (+2u,-2u))
+ \else\ifnum\chemicalangle=90
+ (z1 shifted (-2u,+2u))--(z1 shifted (+2u,-2u))--
+ (z2 shifted (+2u,+2u))--(z2 shifted (-2u,-2u))
+ \else
+ (z1 shifted (+2u,+2u))--(z1 shifted (-2u,-2u))--z2
+ \fi\fi\fi
+ --cycle) rotatedaround(origin,-\chemicalangle)) shifted z0 ;
+ \stopMPdrawing
+ \else
+ \doplotchemicalline
+ \ifnum\chemicalangle>180 \else
+ \ifnum\chemicalangle=90
+ \advance\!!counta by -20 \advance\!!countc by -20
+ \doplotchemicalline
+ \advance\!!counta by 40 \advance\!!countc by 40
+ \else\ifnum\chemicalangle<90
+ \advance\!!countc by -20 \advance\!!countd by +20
+ \doplotchemicalline
+ \advance\!!countc by +40 \advance\!!countd by -40
+ \else
+ \advance\!!counta by 20 \advance\!!countb by 20
+ \doplotchemicalline
+ \advance\!!counta by -40 \advance\!!countb by -40
+ \fi\fi
+ \fi
+ \doplotchemicalline
+ \fi}
+
+\def\plotchemicaldeltaline#1#2#3#4%
+ {\!!counta=#1\!!countb=#2\!!countc=#3\!!countd=#4\relax
+ \ifnum\chemicaldrawingmode=2
+ \global\MPdrawingdonetrue
+ \setchemicalattributes
+ \startMPdrawing
+ x0 := \MPdivten[\chemicalxoffset]u ;
+ y0 := \MPdivten[\chemicalyoffset]u ;
+ x1 := \MPdivten[\the\!!counta]u ;
+ y1 := \MPdivten[\the\!!countb]u ;
+ x2 := \MPdivten[\the\!!countc]u ;
+ y2 := \MPdivten[\the\!!countd]u ;
+ filldraw ((z1--(z2 rotatedaround(z1,5))--(z2 rotatedaround(z1,-5))
+ --cycle) rotatedaround(origin,-\chemicalangle))
+ shifted z0 ;
+ \stopMPdrawing
+ \account{#1}{#2}%
+ \account{#3}{#4}%
+ \else
+ \doplotchemicalline
+ \advance\!!countc by 16 \advance\!!countd by -21
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \advance\!!countc by -8 \advance\!!countd by 14
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \fi}
+
+\def\setchemicallinesegment #1 #2 #3 #4 #5
+ {\setvalue{\s!chemicallinesegment#1}{\plotchemicalline{#2}{#3}{#4}{#5}}}
+
+\def\setchemicalfactorlinesegment #1 #2 #3 #4 #5
+ {\setvalue{\s!chemicallinesegment#1}{\plotchemicalfactorline{#2}{#3}{#4}{#5}}}
+
+\def\getchemicallinesegment[#1][#2]%
+ {\chemicalrotate[#1]%
+ \dochemicaloffset{#1}%
+ \getvalue{\s!chemicallinesegment#2}%
+ \getvalue{\s!chemicallinesegment#21}%
+ \getvalue{\s!chemicallinesegment#22}%
+ \undochemicaloffset}
+
+\def\getprivatechemicallinesegment[#1][#2]%
+ {\chemicalrotate[#1]%
+ \getvalue{\s!chemicallinesegment#2#1}}
+
+\def\doprocesschemicallinesegment#1#2#3#4#5%
+ {\chemicallinetype#1\relax
+ \def\chemicallineposition{#2}%
+ \def\doprocess[##1##2##3##4##5]%
+ {\doifelse{##1}{?}
+ {\doprocess[1..\maxchemical ????]}
+ {\doifchemicalnumber{##1}{#4#5}
+ {\doifelse{##2##3}{..}
+ {\doifchemicalnumber{##4}{#4#5}
+ {#3[##1][#4]%
+ \doifnot{##1}{##4}
+ {\!!counta=##1\relax
+ \advance\!!counta by 1
+ \edef\nextsegment{\the\!!counta}%
+ \doprocess[\nextsegment..##4##5]}}}
+ {#3[##1][#4]%
+ \doifnot{##2}{?}
+ {\doprocess[##2##3##4##5]}}}}}%
+ \doprocess[#5]}
+
+\def\processchemicallinesegment
+ {\doprocesschemicallinesegment0c\getchemicallinesegment}
+
+\def\processchemicalzlinesegment#1#2%
+ {%\doprocesschemicallinesegment0c\getchemicallinesegment{#1}{#2}%
+ \bgroup
+ \def\plotchemicalline{\plotchemicalzline}%
+ \doprocesschemicallinesegment0c\getchemicallinesegment{#1}{#2}%
+ \egroup}
+
+\def\processchemicaldeltalinesegment#1#2%
+ {%\doprocesschemicallinesegment0c\getchemicallinesegment{#1}{#2}%
+ \bgroup
+ \def\plotchemicalline{\plotchemicaldeltaline}%
+ \doprocesschemicallinesegment0c\getchemicallinesegment{#1}{#2}%
+ \egroup}
+
+\def\processprivatechemicallinesegment%
+ {\doprocesschemicallinesegment0c\getprivatechemicallinesegment}
+
+\def\processchemicaldownarrowsegment%
+ {\doprocesschemicallinesegment1c\getchemicallinesegment}
+
+\def\processchemicaluparrowsegment%
+ {\doprocesschemicallinesegment2c\getchemicallinesegment}
+
+\def\processchemicalunrotatedlinesegment#1%
+ {\doprocesschemicallinesegment3{#1}\getchemicallinesegment}
+
+\def\processchemicaldashedlinesegment
+ {\doprocesschemicallinesegment4c\getchemicallinesegment}
+
+\def\plotchemicaldasheddeltaline#1#2#3#4%
+ {\!!counta=#1\!!countb=#2\!!countc=#3\!!countd=#4\relax
+ \ifnum\chemicaldrawingmode=2
+ \global\MPdrawingdonetrue
+ \setchemicalattributes
+ \startMPdrawing
+ x0 := \MPdivten[\chemicalxoffset]u ;
+ y0 := \MPdivten[\chemicalyoffset]u ;
+ x1 := \MPdivten[\the\!!counta]u ;
+ y1 := \MPdivten[\the\!!countb]u ;
+ x2 := \MPdivten[\the\!!countc]u ;
+ y2 := \MPdivten[\the\!!countd]u ;
+ z20 = z2 rotatedaround(z1,+5) ;
+ z21 = z2 rotatedaround(z1,-5) ;
+ draw (z1 rotatedaround(origin,-\chemicalangle)) shifted z0 ;
+ save n ; n := 5 ;
+ for i=1 upto n :
+ draw ((((z20--z21) shifted -z2) shifted (i/n)[z2,z1])
+ rotatedaround(origin,-\chemicalangle)) shifted z0 ;
+ endfor
+ \stopMPdrawing
+ \account{#1}{#2}%
+ \account{#3}{#4}%
+ \else
+ \doplotchemicalline
+ \advance\!!countc by 16 \advance\!!countd by -21
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \advance\!!countc by -8 \advance\!!countd by 14
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \fi}
+
+\def\plotchemicalwavyline#1#2#3#4%
+ {\!!counta=#1\!!countb=#2\!!countc=#3\!!countd=#4\relax
+ \ifnum\chemicaldrawingmode=2
+ \global\MPdrawingdonetrue
+ \setchemicalattributes
+ \startMPdrawing
+ x0 := \MPdivten[\chemicalxoffset]u ;
+ y0 := \MPdivten[\chemicalyoffset]u ;
+ x1 := \MPdivten[\the\!!counta]u ;
+ y1 := \MPdivten[\the\!!countb]u ;
+ x2 := \MPdivten[\the\!!countc]u ;
+ y2 := \MPdivten[\the\!!countd]u ;
+ save d ; pair d ;
+ d := z2 rotatedaround(z1,+5) shifted -z2 ;
+ save n ; n := 4 ;
+ draw ((for i=0 upto n-1 :
+ ((i)/n)[z1,z2] ..
+ ((i+.25)/n)[z1,z2] shifted d ..
+ ((i+.50)/n)[z1,z2] ..
+ ((i+.75)/n)[z1,z2] shifted -d ..
+ endfor
+ z2) rotatedaround(origin,-\chemicalangle)) shifted z0 ;
+ \stopMPdrawing
+ \account{#1}{#2}%
+ \account{#3}{#4}%
+ \else
+ \doplotchemicalline
+ \advance\!!countc by 16 \advance\!!countd by -21
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \advance\!!countc by -8 \advance\!!countd by 14
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \advance\!!countc by -4 \advance\!!countd by 7
+ \doplotchemicalline
+ \fi}
+
+\def\processchemicaldasheddeltalinesegment#1#2%
+ {\bgroup
+ \def\plotchemicalline{\plotchemicaldasheddeltaline}%
+ \doprocesschemicallinesegment0c\getchemicallinesegment{#1}{#2}%
+ \egroup}
+
+\def\processchemicalwavylinesegment#1#2%
+ {\bgroup
+ \def\plotchemicalline{\plotchemicalwavyline}%
+ \doprocesschemicallinesegment0c\getchemicallinesegment{#1}{#2}%
+ \egroup}
+
+\def\processchemicalopenend#1#2%
+ {\doprocesschemicallinesegment0c\doprocesschemicalopenend{#1}{#2}}
+
+\def\doprocesschemicalopenend[#1][#2]%
+ {\chemicalrotate[#1]%
+ \dochemicaloffset{#1}%
+ \ifcase\chemicaldrawingmode
+ \beginpicture
+ \setquadratic\plot
+ 300 0 400 0
+ 500 0 550 75
+ 600 0 650 -75
+ 700 0 750 75
+ 800 0 850 -75
+ 900 0 950 0
+ 1050 0 /
+ \endpicture
+ \or
+ \rput{-\chemicalangle}(\chemicalxoffset,\chemicalyoffset)%
+ {\psline(300,0)(500,0)%
+ \rput(500,0){\psplot[yunit=75,plotstyle=curve]{0}{720}{x sin}}%
+ \psline(950,0)(1050,0)}%
+ \or
+ \global\MPdrawingdonetrue
+ \setchemicalattributes
+ \startMPdrawing
+ x0 := \MPdivten[\chemicalxoffset]u ;
+ y0 := \MPdivten[\chemicalyoffset]u ;
+ draw
+ (((30.0u,0)--(50.0u,0){up}..(55.0u,7.5u)..
+ (60.0u,0)..(65.0u,-7.5u)..(70.0u,0)..
+ (75.0u,7.5u)..(80.0u,0)..(85.0u,-7.5u)..{up}
+ (90.0u,0)--(105.0u,0)) rotatedaround(origin,-\chemicalangle))
+ shifted z0 ;
+ \stopMPdrawing
+ \fi
+ \undochemicaloffset}
+
+% \plotchemicalcircle{#1}{#2}{#3}{#4}
+%
+% #1: lengte van de boog in graden
+% #2: x-coordinaat eindpunt
+% #3: y-coordinaat eindpunt
+
+\newif\ifchemicaldotted
+
+\def\plotchemicalcircle#1#2#3#4#5#6%
+ {\bgroup
+ \ifcase\chemicaldrawingmode
+ \ifchemicaldotted
+ \findlength{\circulararc {#4} degrees from {#5} {#6} center at {0} {0} }%
+ \divide\totalarclength by 6
+ \def\b{\the\totalarclength}%
+ \divide\totalarclength by 2
+ \def\a{\the\totalarclength}%
+ \setdashpattern <\a,\b,\b,\b,\b,\b,\a>
+ \fi
+ \circulararc {#4} degrees from {#5} {#6} center at {0} {0} %
+ \or
+ \ifchemicaldotted
+ \psset{linestyle=dashed}%
+ \fi
+ \rput
+ {-\chemicalangle}(\chemicalxoffset,\chemicalyoffset)%
+ {\psarc(0,0){#3}{#1}{#2}}%
+ \or
+ \global\MPdrawingdonetrue
+ \setchemicalattributes
+ \startMPdrawing
+ r := \MPdivten[#3]*2u;
+ x0 := \MPdivten[\chemicalxoffset]u ;
+ y0 := \MPdivten[\chemicalyoffset]u ;
+ draw ((subpath (#1/45,#2/45) of (fullcircle scaled (r)))
+ rotatedaround (origin,\chemicalangle+150))
+ shifted z0 \ifchemicaldotted dashed withdots \fi ;
+ \stopMPdrawing
+ \fi
+ \egroup}
+
+\def\setchemicalcircsegment #1 #2 #3 #4 #5 #6 #7
+ {\setvalue{\s!chemicalcircsegment#1}{\plotchemicalcircle{#2}{#3}{#4}{#5}{#6}{#7}}}
+
+\def\getchemicalcircsegment[#1][#2]%
+ {\chemicalrotate[#1]%
+ \getvalue{\s!chemicalcircsegment#2}}
+
+\def\doprocesschemicalcircsegment#1#2%
+ {\def\doprocess[##1##2##3##4##5]%
+ {\doifelse{##1}{?}
+ {\doprocess[1..\maxchemical ????]}
+ {\doifchemicalnumber{##1}{#1#2}
+ {\doifelse{##2##3}{..}
+ {\doifchemicalnumber{##4}{#1#2}
+ {\getchemicalcircsegment[##1][#1]%
+ \doifnot{##1}{##4}
+ {\!!counta=##1\relax
+ \advance\!!counta by 1
+ \edef\nextsegment{\the\!!counta}%
+ \doprocess[\nextsegment..##4##5]}}}
+ {\getchemicalcircsegment[##1][#1]%
+ \doifnot{##2}{?}
+ {\doprocess[##2##3##4##5]}}}}}%
+ \doprocess[#2]}
+
+\def\processchemicalcircsegment%
+ {\chemicaldottedfalse\doprocesschemicalcircsegment}
+
+\def\processchemicaldottsegment%
+ {\chemicaldottedtrue\doprocesschemicalcircsegment}
+
+\let\endchemicalpicture = \relax
+\let\checkchemicalpicture = \relax
+\let\nomoreaccounting = \relax
+
+\newif\ifchemicalpicture
+
+\def\beginchemicalpicture#1% NO PSTRICKS SUPPORT YET
+ {\checkchemicalpicture
+ \bgroup % DOES NOT HANDLE AUTOWIDTH/HEIGHT
+ \chemicalpicturetrue
+ \processchemical[#1]}
+
+\def\setchemicalpicture#1#2%
+ {\chemicalpicturefalse
+ \def\endchemicalpicture%
+ {\@@endchemicallocalpicture{#1}{#2}%
+ \egroup
+ \ifnum\chemicaldrawingmode=1
+ \rput
+ {-\chemicalangle}(\chemicalxoffset,\chemicalyoffset)%
+ {\expanded{\rput[\chemicalloca]{\chemicalangle}(#1,#2){\box\nextbox}}}%
+ \else
+ \expanded{\put{\box\nextbox}[\chemicalloca] at {#1} {#2} }
+ \fi
+ \egroup}%
+ \def\checkchemicalpicture%
+ {\ifx\endchemicalpicture\relax \else
+ \writestatus{ppchtex}{missing end of picture (PE)}%
+ \endchemicalpicture
+ \fi}%
+ \setbox\nextbox=\hbox\bgroup
+ \@@beginchemicallocalpicture
+ % alternatief: gewoon accounting, en zelf l,r afhandelen
+ \ifnum\chemicaldrawingmode=1
+ % njet
+ \else
+ \accountingon
+ \let\nomoreaccounting=\accountingoff
+ \fi}
+
+\def\doskipchemical[#1][#2]%
+ {{\tt[ppchtex]}}
+
+\def\skipchemical%
+ {\dodoubleargument\doskipchemical}
+
+\def\complexchemical% met \expandafter
+ {\ifinchemical
+ \expandafter\dochemical
+ \else
+ \writestatus{ppchtex}{the [][]-alternative is not permitted here}%
+ \expandafter\skipchemical
+ \fi}
+
+\newif\ifinnerchemical
+
+\def\dosimplechemical#1#2#3%
+ {\doifelsedefined{\??chemical\c!location}
+ {\writestatus{ppchtex}{the {}{}-alternative is not permitted here}}
+ {\ifinnerchemical
+ \let\chemicalsign = \chemicalinnersign
+ \let\chemicalmolecule = \chemicalinnermolecule
+ \let\chemicalsinglearrow = \chemicalsingleinnerarrow
+ \let\chemicaldoublearrow = \chemicaldoubleinnerarrow
+ \let\chemicaltwintiparrow = \chemicaltwintipinnerarrow
+ \else
+ \let\chemicalsign = \chemicaloutersign
+ \let\chemicalmolecule = \chemicaloutermolecule
+ \let\chemicalsinglearrow = \chemicalsingleouterarrow
+ \let\chemicaldoublearrow = \chemicaldoubleouterarrow
+ \let\chemicaltwintiparrow = \chemicaltwintipouterarrow
+ \fi
+ \disablechemicalspecials
+ \processallactionsinset
+ [#1]
+ [ HIGH=>\sethighsubscripts,
+ LOW=>\setlowsubscripts,
+ PLUS=>\chemicalsign{+},
+ GIVES=>\chemicalsinglearrow{#2}{#3},
+ EQUILIBRIUM=>\chemicaldoublearrow{#2}{#3},
+ MESOMERIC=>\chemicaltwintiparrow{#2}{#3},
+ SINGLE=>\singlechemicalbond,
+ DOUBLE=>\doublechemicalbond,
+ TRIPLE=>\triplechemicalbond,
+ +=>\chemicalsign{+},
+ ->=>\chemicalsinglearrow{#2}{#3},
+ <->=>\chemicaldoublearrow{#2}{#3},
+ <>=>\chemicaltwintiparrow{#2}{#3},
+ -=>\singlechemicalbond,
+ --=>\doublechemicalbond,
+ ---=>\triplechemicalbond,
+ \s!unknown=>\enablechemicalspecials
+ \chemicalmolecule{\commalistelement}{#2}{#3}]}}
+
+\def\dosimplechemicalA#1#2#3% % evt: {#1,\relax}
+ {\let\chemicalspace=\relax
+ \@EA\dosimplechemical\@EA{\@@chemicalchemicaloffset,#1}{#2}{#3}%
+ \egroup}
+
+\def\dosimplechemicalB#1#2#3%
+ {\dosimplechemical{#1}{#2}{#3}%
+ \egroup}
+
+\def\dosimplechemicalC#1#2#3%
+ {$\simplechemical{#1}{#2}{#3}$%
+ \egroup} % erbij
+
+\def\simplechemical
+ {\ifinner
+ \innerchemicaltrue
+ \else
+ \innerchemicalfalse
+ \fi
+ \bgroup
+ \catcode`\^=\superscriptcatcode % t.b.v. \enableduplication
+ \catcode`\_=\subscriptcatcode % t.b.v. de zekerheid
+ \ifmmode
+ \ifinnerchemical
+ \def\next{\dotriplegroupempty\dosimplechemicalA}%
+ \else
+ \def\next{\dotriplegroupempty\dosimplechemicalB}%
+ \fi
+ \else
+ \def\next{\dotriplegroupempty\dosimplechemicalC}%
+ \fi
+ \next}
+
+\definecomplexorsimple\chemical
+
+\def\dogotochemical#1#2%
+ {\def\dowithchemical% % experiment
+ {\localgotochemical{#1}}% % experiment
+ \chemical} % experiment
+
+\def\gotochemical% % experiment
+ {\dosingleargument\dogotochemical} % experiment
+
+\def\dododochemical#1[#2][#3]% % experiment
+ {\def\simpledododochemical% % experiment
+ {#1[#2][#3]}% % experiment
+ \def\complexdododochemical[##1]% % experiment
+ {\def\dowithchemical% % experiment
+ {\localthisischemical{#2}}% % experiment
+ #1[#3][##1]}% % experiment
+ \complexorsimple\dododochemical} % experiment
+
+\def\dodochemical[#1][#2]%
+ {\ignorespaces
+ \ifinchemical
+ \drawchemical[#1][#2]%
+ \ignorespaces
+ \else
+ \startchemical[\c!location=\v!intext]%
+ \drawchemical[#1][#2]%
+ \expandafter\stopchemical
+ \fi
+ \ignorespaces}
+
+\def\dochemical[#1]%
+ {\def\simpledochemical%
+ {\@@writechemicalstate{ppchtex}{[#1][]}%
+ \dodochemical[#1][]}%
+ %
+ \def\complexdochemical[##1]%
+ {\@@writechemicalstate{ppchtex}{[#1][##1]}%
+ \txtchemical=0%
+ \dodochemical[#1][##1]}%
+ %
+ \def\complexdochemical[##1]% % experiment
+ {\@@writechemicalstate{ppchtex}{[#1][##1]}% % experiment
+ \txtchemical=0% % experiment
+ \dododochemical\dodochemical[#1][##1]}% % experiment
+ %
+ \complexorsimple\dochemical}
+
+% \processlocalchemicals{#1}
+%
+% #1: commando's
+
+\def\dodoprocesschemical#1%
+ {\processchemical[#1????]}
+
+\def\processlocalchemicals#1%
+ {\processcommalist[#1]\dodoprocesschemical}
+
+% \drawchemical[#1][#2]
+%
+% #1: bindingen enz.
+% #2: atomen enz.
+
+\def\localdodochemical[#1][#2]%
+ {\@@writechemicalstate{ppchtex}{[#1][#2]}%
+ %\bgroup % koppelen en afmetingen gaat fout, vandaar:
+ \advance\levchemical 1
+ \letvalue{\??chemical\s!unknown\the\levchemical}\unknownchemical
+ \setevalue{\??chemical\c!text\the\levchemical}{\the\txtchemical}%
+ \txtchemical=0
+ \dodochemical[#1][#2]%
+ % \@EA\txtchemical\@EA\csname\??chemical\c!text\the\levchemical\endcsname
+ \txtchemical\csname\??chemical\c!text\the\levchemical\endcsname
+ \@EA\let\@EA\unknownchemical\csname\??chemical\s!unknown\the\levchemical\endcsname
+ \advance\levchemical -1
+ %\egroup
+ \ignorespaces}
+
+\def\drawchemical[#1][#2]%
+ {\ignorespaces
+ \def\dodochemical[##1][##2]%
+ {\drawchemical[##1][##2]%
+ \ignorespaces}%
+ \def\dochemical[##1]%
+ {\def\simpledochemical%
+ {\@@writechemicalstate{ppchtex}{[##1][#2]}%
+ \dodochemical[##1][#2]%
+ \ignorespaces}%
+ \def\complexdochemical[####1]%
+ {\dododochemical\localdodochemical[##1][####1,#2]}%
+ \complexorsimple\dochemical}%
+ \doif\@@chemicalstate\v!start
+ {\doifelse\chemicalname\s!unknown
+ {\getvalue{\s!executechemical\defaultchemical}[#2]}
+ {\getvalue{\s!executechemical\chemicalname}[#2]}%
+ \def\unknownchemical##1%
+ {\processunknownchemical[##1][#2]}%
+ \processcommalist[\@@chemicaloffset,#1]\dodoprocesschemical}%
+ \ignorespaces}
+
+\unexpanded\def\chemicaloxidation#1#2#3%
+ {\chemicaltop
+ {\ifnum#20=0
+ 0%
+ \else
+ #1\expandafter\uppercase\expandafter{\romannumeral#2}%
+ \fi}
+ {#3}}
+
+\def\chemicaltfraction{\ifinchemical.60\else.8\fi}
+\def\chemicalbfraction{\ifinchemical.45\else.6\fi}
+\def\chemicallfraction{\ifinchemical.1\else.1\fi}
+\def\chemicalrfraction{\ifinchemical.1\else.1\fi}
+
+\def\chemicaltighttext
+ {\def\chemicaltfraction{\ifinchemical.3\else.6\fi}%
+ \def\chemicalbfraction{\ifinchemical.2\else.4\fi}%
+ \def\chemicallfraction{\ifinchemical 0\else 0\fi}%
+ \def\chemicalrfraction{\ifinchemical 0\else 0\fi}}
+
+\def\dochemicaltop#1#2#3#4%
+ {\vbox
+ {\@@dochemicalcolor
+ \baselineskip=\chemicaltfraction\baselineskip \lineskip0pt
+ \halign
+ {#1###2\cr
+ $\@@dochemicalstyle{\scriptscriptstyle#3}$\cr
+ $\@@dochemicalstyle{\@@currentchemicalformat#4}$\cr}}}
+
+\def\dochemicalbottom#1#2#3#4%
+ {\vtop
+ {\@@dochemicalcolor
+ \baselineskip=\chemicalbfraction\baselineskip \lineskip0pt
+ \halign
+ {#1###2\cr
+ $\@@dochemicalstyle{\@@currentchemicalformat#4}$\cr
+ $\@@dochemicalstyle{\scriptscriptstyle#3}$\cr}}}
+
+\def\chemicalleft#1#2%
+ {\hbox
+ {\@@dochemicalcolor
+ $\@@dochemicalstyle{\scriptscriptstyle#1}$%
+ $\@@dochemicalstyle{\@@currentchemicalformat\hskip\chemicallfraction em#2}$}}
+
+\def\chemicalright#1#2%
+ {\hbox
+ {\@@dochemicalcolor
+ $\@@dochemicalstyle{\@@currentchemicalformat#2\hskip\chemicalrfraction em}$%
+ $\@@dochemicalstyle{\scriptscriptstyle#1}$}}
+
+\def\chemicalcentered#1%
+ {\setbox0=\hbox{$\@@dochemicalstyle{\scriptscriptstyle#1}$}%
+ \setbox2=\hbox{$\@@dochemicalstyle{\@@currentchemicalformat C}$}%
+ \dimen0=.5\ht2
+ \advance\dimen0 by -.5\ht0
+ \advance\dimen0 by \dp0
+ \hbox{\@@dochemicalcolor\raise\dimen0\box0}}
+
+\def\chemicalleftcentered#1#2%
+ {\hbox
+ {\@@dochemicalcolor
+ \chemicalcentered{#1}%
+ $\@@dochemicalstyle{\@@currentchemicalformat\hskip\chemicallfraction em#2}$}}
+
+\def\chemicalrightcentered#1#2%
+ {\hbox
+ {\@@dochemicalcolor
+ $\@@dochemicalstyle{\@@currentchemicalformat#2\hskip\chemicalrfraction em}$%
+ \chemicalcentered{#1}}}
+
+\def\chemicaltop {\dochemicaltop \hss \hss }
+\def\chemicallefttop {\dochemicaltop \relax \hss }
+\def\chemicalrighttop {\dochemicaltop \hss \relax}
+\def\chemicalbottom {\dochemicalbottom \hss \hss }
+\def\chemicalleftbottom {\dochemicalbottom \relax \hss }
+\def\chemicalrightbottom {\dochemicalbottom \hss \relax}
+
+\def\chemicaltopleft #1{\chemicalleft {\chemicallefttop {#1}{}}}
+\def\chemicalbottomleft #1{\chemicalleft {\chemicalleftbottom{#1}{}}}
+\def\chemicaltopright #1{\chemicalright{\chemicallefttop {#1}{}}}
+\def\chemicalbottomright#1{\chemicalright{\chemicalleftbottom{#1}{}}}
+
+\def\chemicalsmashedleft#1%
+ {\hbox\bgroup
+ \@@dochemicalcolor
+ \setbox0=\hbox{$\@@dochemicalstyle{\@@currentchemicalformat C}$}%
+ \setbox2=\hbox{$\@@dochemicalstyle{\@@currentchemicalformat#1}$}%
+ \wd2=\wd0
+ \box2
+ \egroup}
+
+\def\chemicalsmashedmiddle#1%
+ {\hbox\bgroup
+ \@@dochemicalcolor
+ \setbox0=\hbox{$\@@dochemicalstyle{\@@currentchemicalformat C}$}%
+ \setbox2=\hbox{$\@@dochemicalstyle{\@@currentchemicalformat#1}$}%
+ \hbox{\hskip-.5\wd2\hskip.5\wd0\box2}
+ \egroup}
+
+\def\chemicalsmashedright#1%
+ {\hbox\bgroup
+ \@@dochemicalcolor
+ \setbox0=\hbox{$\@@dochemicalstyle{\@@currentchemicalformat C}$}%
+ \setbox2=\hbox{$\@@dochemicalstyle{\@@currentchemicalformat#1}$}%
+ \hbox to \wd0{\hskip-\wd2\hskip\wd0\box2}%
+ \egroup}
+
+\def\+{\tabalign} % is \long in Plain
+
+\def\chemicalforever#1#2%
+ {\bgroup
+ \setbox0=\hbox
+ {\@@dochemicalcolor
+ $\@@dochemicalstyle{\scriptscriptstyle\hskip-.15em#2}$}%
+ \wd0=0pt
+ \big#1\normapsubscript{\hskip.1em\box0}%
+ \egroup}
+
+\def\disablechemicalspecials%
+ {\def\+##1{##1}\def\-##1{##1}%
+ \def\[{[}\def\]{]}%
+ \def\1{}\def\2{}\def\3{}\def\4{}\def\5{}\def\6{}\def\7{}%
+ \def\X{}%
+ \def\T{}\def\B{}\def\L{}\def\R{}\def\LC{}\def\RC{}%
+ \def\TL{}\def\BL{}\def\TR{}\def\BR{}%
+ \def\LT{}\def\LB{}\def\RT{}\def\RB{}%
+ \def\SL{}\def\SM{}\def\SR{}}
+
+\def\enablechemicalspecials%
+ {\def\+{\dodoublegroupempty\chemicaloxidation{+}}% {} needed!
+ \def\-{\dodoublegroupempty\chemicaloxidation{-}}% {} needed!
+ \def\[{\dodoublegroupempty\chemicalforever {[}}% {} needed!
+ \def\]{\dodoublegroupempty\chemicalforever {]}}% {} needed!
+ \def\1{\chemicaloxidation\relax1}%
+ \def\2{\chemicaloxidation\relax2}%
+ \def\3{\chemicaloxidation\relax3}%
+ \def\4{\chemicaloxidation\relax4}%
+ \def\5{\chemicaloxidation\relax5}%
+ \def\6{\chemicaloxidation\relax6}%
+ \def\7{\chemicaloxidation\relax7}%
+ \def\X{\chemicaltighttext}%
+ \def\T{\chemicaltop}%
+ \def\B{\chemicalbottom}%
+ \def\L{\chemicalleft}%
+ \def\LC{\chemicalleftcentered}%
+ \def\R{\chemicalright}%
+ \def\RC{\chemicalrightcentered}%
+ \def\TL{\chemicaltopleft}%
+ \def\BL{\chemicalbottomleft}%
+ \def\TR{\chemicaltopright}%
+ \def\BR{\chemicalbottomright}%
+ \def\LT{\chemicallefttop}%
+ \def\LB{\chemicalleftbottom}%
+ \def\RT{\chemicalrighttop}%
+ \def\RB{\chemicalrightbottom}%
+ \def\SL{\chemicalsmashedleft}%
+ \def\SM{\chemicalsmashedmiddle}%
+ \def\SR{\chemicalsmashedright}}
+
+% \reversechemical#1#2#3
+%
+% #1: prefix
+% #2: volgnummer enz
+% #3: tegengestelde volgnummers
+
+\def\reversechemical#1#2#3%
+ {\def\doprocess[##1##2]%
+ {\doifchemicalnumber{##1}{#1#2}%
+ {\getfromcommalist[#3][##1]%
+ \let\reversechemicalaction=\commalistelement
+ \processchemical[#1\reversechemicalaction##2]}}%
+ \doprocess[#2]}
+
+% \processunknownchemical[#1????][#2]
+%
+% #1: bindingen enz.
+% #2: atomen enz.
+
+\def\defaultchemical%
+ {SIX}
+
+\def\processunknownchemical[#1????][#2]%
+ {\processaction
+ [#1]
+ [ SAVE=>\executechemicalSAVE,
+ RESTORE=>\executechemicalRESTORE,
+ HIGH=>\sethighsubscripts,
+ LOW=>\setlowsubscripts,
+ \s!default=>,
+ \s!unknown=>\doifelsedefined{\s!executechemical#1}
+ {\def\chemicalrotation{1}%
+ \def\chemicaloffset{0}%
+ \doifdefined{\s!executechemical#1}
+ {\getvalue{\s!executechemical#1}[#2]}%
+ \@@chemicalpostponed}
+ {\getpredefinedchemical{#1}}]}
+
+\newcount\chemicalstack
+
+\setvalue{\s!chemical\c!x1}{0}
+\setvalue{\s!chemical\c!y1}{0}
+
+\def\executechemicalSAVE
+ {%\writestatus{ppchtex}{saving \the\horchemical,\the\verchemical}%
+ \advance\chemicalstack by 1
+ \letvalue {\s!chemical n\the\chemicalstack}=\chemicalname
+ %\letvalue {\s!chemical p\the\chemicalstack}=\@@chemicalpostponed
+ \setevalue{\s!chemical x\the\chemicalstack}{\the\horchemical}%
+ \setevalue{\s!chemical y\the\chemicalstack}{\the\verchemical}}
+
+\def\restorechemicalvalues#1%
+ {\let\oldprocesschemical=\processchemical
+ \doifdefined{\s!executechemical#1}{\getvalue{\s!executechemical#1}[]}%
+ \let\processchemical=\oldprocesschemical}
+
+\def\executechemicalRESTORE
+ {\ifnum\chemicalstack=0\relax
+ \horchemical=\getvalue{\s!chemical x1}\relax
+ \verchemical=\getvalue{\s!chemical y1}\relax
+ \else
+ \restorechemicalvalues{\getvalue{\s!chemical n\the\chemicalstack}}%
+ %\@EA\let\@EA\@@chemicalpostponed\@EA=\csname\s!chemical p\the\chemicalstack\endcsname
+ \let\@@chemicalpostponed=\relax
+ \horchemical=\getvalue{\s!chemical x\the\chemicalstack}\relax
+ \verchemical=\getvalue{\s!chemical y\the\chemicalstack}\relax
+ \advance\chemicalstack by -1
+ \fi
+ \restorechemicalcoordinates}
+
+% De onderstaande macro's zijn verantwoordelijk voor het zetten
+% van de + en pijlen. De +, en dus ook de pijlen, worden omhoog
+% gehaald. Dit oogt m.i. fraaier.
+
+\def\chemicalinnerclip#1%
+ {{\setbox0=\hbox{#1}\ht0\ht\strutbox\dp0\dp\strutbox\box0}}
+
+\def\chemicalraise#1#2%
+ {\chemicalinnerclip
+ {\setbox0=\hbox{$#1+$}%
+ \raise\dp0\hbox{$#1#2$}}}
+
+\def\chemicalinnersign#1% todo: \@@chemicaltextcolor
+ {\chemicalraise{\@@localchemicalstyle}{#1}}
+
+\def\chemicaloutersign#1%
+ {\chemicalraise{}{\@@dochemicalcolor#1}}
+
+\def\chemicalsingleinnerarrow#1#2%
+ {\chemicalraise{\@@localchemicalstyle}{\longrightarrow}}
+
+\def\chemicaldoubleinnerarrow#1#2% todo: \@@chemicaltextcolor
+ {\chemicalinnerclip
+ {\lower.2ex\hbox
+ {\setbox0=\hbox{$\@@localchemicalstyle\longrightarrow$}%
+ \setbox2=\hbox{$\@@localchemicalstyle\longleftarrow$}%
+ \wd0=0pt\raise\ht0\box0\box2}}}
+
+\def\chemicaltwintipinnerarrow#1#2% todo: \@@chemicaltextcolor
+ {\chemicalinnerclip
+ {\setbox0=\hbox{\chemicalraise{\@@localchemicalstyle}{\longrightarrow}}%
+ \setbox2=\hbox{\chemicalraise{\@@localchemicalstyle}{\longleftarrow}}%
+ \wd0=0pt\box0\box2}}
+
+\def\dochemicalouterarrow#1#2#3%
+ {\bgroup
+ \setbox0=\hbox{$\longrightarrow$}%
+ \setbox2=\hbox{$\@@dochemicalstyle{\scriptstyle\quad#2\quad}$}%
+ \setbox4=\hbox{$\@@dochemicalstyle{\scriptstyle\quad#3\quad}$}%
+ \dimen2=\wd0 % \dimen0 is used elsewhere
+ \ifdim\wd2>\dimen2 \dimen0=\wd2 \fi
+ \ifdim\wd4>\dimen2 \dimen0=\wd4 \fi
+ \chemicaloutermolecule
+ {#1}
+ {\ifdim\ht2>\zeropoint\box2\fi} % expands to \empty in test
+ {\ifdim\ht4>\zeropoint\box4\fi}% % expands to \empty in test
+ \egroup}
+
+\def\chemicalsingleouterarrow
+ {\dochemicalouterarrow
+ {\hbox to \dimen2{\rightarrowfill}}}
+
+\def\chemicaldoubleouterarrow
+ {\dochemicalouterarrow
+ {\lower.5\ht0\vbox
+ {\offinterlineskip
+ \hbox to \dimen2{\rightarrowfill}
+ \hbox to \dimen2{\leftarrowfill}}}}
+
+\def\chemicaltwintipouterarrow
+ {\dochemicalouterarrow
+ {\hbox
+ {\hbox to \dimen2{\rightarrowfill}%
+ \hskip-\dimen2
+ \hbox to \dimen2{\leftarrowfill}}}}
+
+\def\chemicalinnermolecule#1#2#3% no mathop here, can generate space
+ {\chemicalspace % todo: \@@chemicaltextcolor
+ \chemicalinnerclip
+ {\dosetsubscripts
+ $\@@dochemicalstyle{\@@localchemicalstyle\strut#1}$%
+ \doresetsubscripts}%
+ \chemicalspace}
+
+\def\chemicaloutermolecule#1#2#3%
+ {\chemicalspace
+ \bgroup
+ \@@dochemicalcolor
+ \setbox0=\hbox % else the font is reset
+ {\dosetsubscripts
+ \hbox{$\@@dochemicalstyle{\strut#1}$}%
+ \doresetsubscripts}%
+ \mathop{\box0}%
+ \ifthirdargument
+ \doifnot{#2}{}{\normapsuperscript{\@@dochemicalstyle{\strut#2}}}%
+ \doifnot{#3}{}{\normapsubscript {\@@dochemicalstyle{\strut#3}}}%
+ \else
+ \doifnot{#2}{}
+ {\normapsubscript{\@@dochemicalstyle{\strut#2}}}%
+ \fi
+ \egroup
+ \chemicalspace}
+
+\def\chemicalsinglepicturearrow#1%
+ {\lower.5ex\hbox
+ {\@@dochemicalstyle
+ $\chemicalspace
+ \buildrel
+ \@@dochemicalstyle{\scriptstyle\quad#1\quad}%
+ \over{\overrightarrow
+ {\hphantom{\@chemicalstyle{\scriptstyle\quad#1\quad}}}}%
+ \chemicalspace$}}
+
+\def\chemicaldoublepicturearrow#1%
+ {\lower.5ex\hbox
+ {\@@dochemicalstyle
+ $\chemicalspace
+ \buildrel
+ \@@dochemicalstyle{\scriptstyle\quad#1\quad}%
+ \over{\overrightarrow{\overleftarrow
+ {\hphantom{\@@dochemicalstyle{\scriptstyle\quad#1\quad}}}}}%
+ \chemicalspace$}}
+
+% Bij de in-line bindingen wordt gebruik gemaakt van
+% een \hrule. De maatvoering wordt bepaald door een
+% kunstmatige em (\wd0).
+
+\def\somechemicalbond%
+ {\hrule width \wd0 height .4pt}
+
+\def\dochemicalbonds#1#2#3% todo: \@@chemicaltextstyle
+ {{\setbox0=\hbox
+ {${\@@localchemicalstyle M}$}%
+ \vbox to \ht0
+ {\@@dochemicalcolor
+ \hsize\wd0
+ \vskip.1\wd0#1\vfill#2\vfill#3\vskip.1\wd0}}}
+
+\def\singlechemicalbond%
+ {\dochemicalbonds{}{\somechemicalbond}{}}
+
+\def\doublechemicalbond%
+ {\dochemicalbonds{\somechemicalbond}{}{\somechemicalbond}}
+
+\def\triplechemicalbond%
+ {\dochemicalbonds{\somechemicalbond}{\somechemicalbond}{\somechemicalbond}}
+
+% In plaats van \def\naam{\chemie[...]...} kan beter gebruik
+% worden gemaakt van het commando
+%
+% \definieerchemie[naam]{commando's}
+%
+% De naam krijgt, om problemen met bestaande macro's te
+% voorkomen, een prefix. Bij het ophalen van een commando
+% worden beide definities afgehandeld.
+
+\def\dodefinechemical[#1]#2%
+ {\doifdefined{\??chemical#1}
+ {\writestatus{ppchtex}{chemical definition #1 is redefined}}%
+ \setvalue{\??chemical#1}{#2}}
+
+\unexpanded\def\definechemical%
+ {\dosingleargument\dodefinechemical}
+
+\def\getpredefinedchemical#1%
+ {\doifelsedefined{\??chemical#1}
+ {\getvalue{\??chemical#1}}
+ {\doifelsedefined{#1}
+ {\getvalue{#1}}
+ {\writestatus{ppchtex}{unknown chemical definition #1}}}}
+
+% Hieronder zijn de definities van de structuren opgenomen. De
+% naam van de structuur is als volgt opgebouwd:
+%
+% \executechemicalNUMBER[#1]
+%
+% waarbij [#1] betrekking heeft op de tekstelementen van \chemie,
+% de [tweede lijst] dus.
+%
+% De aan \chemie[#1][#2] meegegeven lijst van segmenten wordt
+% deels door de in \execute gedefinieerde macro's afgehandeld,
+% deels door algemene macro's. Segmenten hebben de vorm:
+%
+% [+|-|]identifier[X|XYZ|X..Y]
+%
+% Voorbeelden van segmenten zijn:
+%
+% R1
+% R1..4
+% R135
+% -R1
+% +R35
+
+\setchemicalmaximum 0
+
+\def\processchemical[#1]%
+ {\unknownchemical{#1}}
+
+\def\setchemicalname#1 %
+ {\def\chemicalname{#1}}
+
+\let\chemicalname=\s!unknown
+
+% Vooruitlopend op een gedetailleerde documentatie, zijn hier
+% vast enkele gebruikte afmetingen:
+%
+% lengte radikalen : 500
+% afstand radikalen : 100
+% afstand dubbele radikalen : 260
+% afstand substituenten : +125
+
+
+
+
+\def\executechemicalONE[#1]%
+ {\setchemicalname ONE
+ %
+ \setchemicalmaximum 8
+ \setchemicaldistance 0
+ \setchemicalsubstitute 625
+ \setchemicaldirection 303
+ %
+ \setchemicalrotation 1 1 0 1 0 1 0 1 0
+ \setchemicalrotation 2 0.707 -0.707 0.707 -0.707 0.707 -0.707 0.707 -0.707
+ \setchemicalrotation 3 0 -1 0 -1 0 -1 0 -1
+ \setchemicalrotation 4 -0.707 -0.707 -0.707 -0.707 -0.707 -0.707 -0.707 -0.707
+ \setchemicalrotation 5 -1 0 -1 0 -1 0 -1 0
+ \setchemicalrotation 6 -0.707 0.707 -0.707 0.707 -0.707 0.707 -0.707 0.707
+ \setchemicalrotation 7 0 1 0 1 0 1 0 1
+ \setchemicalrotation 8 0.707 0.707 0.707 0.707 0.707 0.707 0.707 0.707
+ %
+ \setchemicalangle 1 0 90 180 270
+ \setchemicalangle 2 45 135 225 315
+ \setchemicalangle 3 90 180 270 0
+ \setchemicalangle 4 135 225 315 45
+ \setchemicalangle 5 180 270 0 90
+ \setchemicalangle 6 225 315 45 135
+ \setchemicalangle 7 270 0 90 180
+ \setchemicalangle 8 315 45 135 225
+ %
+ \setchemicaltranslate 1 -1000 0
+ \setchemicaltranslate 2 -1000 1000
+ \setchemicaltranslate 3 0 1000
+ \setchemicaltranslate 4 1000 1000
+ \setchemicaltranslate 5 1000 0
+ \setchemicaltranslate 6 1000 -1000
+ \setchemicaltranslate 7 0 -1000
+ \setchemicaltranslate 8 -1000 -1000
+ %
+ \setchemicallinesegment SB 300 0 700 0
+ \setchemicallinesegment DB1 300 50 700 50
+ \setchemicallinesegment DB2 300 -50 700 -50
+ %
+ %setchemicallinesegment EP 200 125 200 -125
+ \setchemicalfactorlinesegment EP 200 125 200 -125
+ %
+ \setchemicaltextelement ES 200 0
+ \setchemicaltextelement ED1 200 50
+ \setchemicaltextelement ED2 200 -50
+ \setchemicaltextelement ET1 200 75
+ \setchemicaltextelement ET2 200 0
+ \setchemicaltextelement ET3 200 -75
+ \setchemicaltextelement HB1 300 0
+ \setchemicaltextelement HB2 475 0
+ \setchemicaltextelement HB3 650 0
+ %
+ \setchemicaltextelement Z 800 0
+ \setchemicaltextelement RZ 950 0
+ \setchemicaltextelement ZN 500 0
+ \setchemicaltextelement ZTN 500 150
+ \setchemicaltextelement ZBN 500 -150
+ %
+ \def\processchemical[##1##2##3##4##5]%
+ {\doprocesschemical[##1##2##3##4##5]
+ {\processaction
+ [##1##2##3##4##5]
+ [ PB:##4##5=>\beginchemicalpicture{##4##5},
+ PE????=>\endchemicalpicture,
+ SUB##4##5=>\processchemicalsubstitute{##4##5},
+ ADJ##4##5=>\processchemicaldistance{##4##5},
+ MOV##4##5=>\processchemicaltranslate{##4##5},
+ DIR##4##5=>\processchemicaldirection{##4##5},
+ OFF##4##5=>\processchemicaloffset{##4##5},
+ CCD##4##5=>\processchemicaldottsegment{CC}{##4##5},
+ LDD##4##5=>\processchemicaldashedlinesegment{DB1}{##4##5}%
+ \processchemicallinesegment{DB2}{##4##5},
+ RDD##4##5=>\processchemicallinesegment{DB1}{##4##5}%
+ \processchemicaldashedlinesegment{DB2}{##4##5},
+ OF##3:##5=>\processchemicalphantom{##3}{##5},
+ OE##3##4##5=>\processchemicalopenend{OE}{##3##4##5},
+ EP##3##4##5=>\processchemicallinesegment{EP}{##3##4##5},
+ ES##3##4##5=>\processchemicaltextconstant{ES}{##3##4##5}{\hbox{$\cdot$}}{0},
+ ED##3##4##5=>\processchemicaltextconstant{ED}{##3##4##5}{\hbox{$\cdot$}}{0},
+ ET##3##4##5=>\processchemicaltextconstant{ET}{##3##4##5}{\hbox{$\cdot$}}{0},
+ HB##3##4##5=>\processchemicaltextconstant{HB}{##3##4##5}{\hbox{$\cdot$}}{0},
+ SB##3##4##5=>\processchemicallinesegment{SB}{##3##4##5},
+ DB##3##4##5=>\processchemicallinesegment{DB}{##3##4##5},
+ SB##3##4##5=>\processchemicallinesegment{SB}{##3##4##5},
+ BB##3##4##5=>\processchemicaldeltalinesegment{SB}{##3##4##5},
+ BD##3##4##5=>\processchemicaldasheddeltalinesegment{SB}{##3##4##5},
+ BW##3##4##5=>\processchemicalwavylinesegment{SB}{##3##4##5},
+ SD##3##4##5=>\processchemicaldashedlinesegment{SB}{##3##4##5},
+ TB##3##4##5=>\processchemicallinesegment{SB}{##3##4##5}%
+ \processchemicallinesegment{DB}{##3##4##5},
+ CZ##3##4##5=>\processchemicaltextelement{RZ}{##3##4##5}{#1}{0}{},
+ ZTN##4##5=>\processchemicalsmalltextconstant{ZTN}{##4##5}{\chemicaltextelementnumber}{0},
+ ZTT##4##5=>\processchemicalsmalltextelement{ZTN}{##4##5}{#1}{0}{},
+ ZBN##4##5=>\processchemicalsmalltextconstant{ZBN}{##4##5}{\chemicaltextelementnumber}{0},
+ ZBT##4##5=>\processchemicalsmalltextelement{ZBN}{##4##5}{#1}{0}{},
+ ZN##3##4##5=>\processchemicaltextconstant{ZN}{##3##4##5}{\chemicaltextelementnumber}{0},
+ ZT##3##4##5=>\processchemicaltextelement{ZN}{##3##4##5}{#1}{0}{},
+ Z##2##3##4##5=>\processchemicaltextelement{Z}{##2##3##4##5}{#1}{0}
+ {l,l,t,r,r,r,b,l},
+ \s!unknown=>\unknownchemical{##1##2##3##4##5}]}}}
+
+\def\executechemicalTHREE[#1]%
+ {\setchemicalname THREE
+ %
+ \setchemicalmaximum 3
+ \setchemicaldistance 289
+ \setchemicalsubstitute 952
+ %
+ \setchemicalrotation 1 1 0 0 -1 -1 0 0 1
+ \setchemicalrotation 2 -0.5 -0.866 -0.866 0.5 0.5 0.866 0.866 -0.5
+ \setchemicalrotation 3 -0.5 0.866 0.866 0.5 0.5 -0.866 -0.866 -0.5
+ %
+ \setchemicalangle 1 0 90 180 270
+ \setchemicalangle 2 120 210 300 30
+ \setchemicalangle 3 240 330 60 150
+ %
+ \setchemicaltranslate 1 -1000 0
+ \setchemicaltranslate 2 0 1000
+ \setchemicaltranslate 3 1000 0
+ \setchemicaltranslate 4 0 -1000
+ %
+ \setchemicallinesegment B 577 0 -289 -500
+ \setchemicallinesegment SB 352 -130 -64 -370
+ \setchemicallinesegment -SB 352 -130 -289 -500
+ \setchemicallinesegment +SB 577 0 -64 -370
+ \setchemicallinesegment DB1 327 -87 -89 -327
+ \setchemicallinesegment DB2 377 -172 -39 -413
+ \setchemicallinesegment R 577 0 1077 0
+ \setchemicallinesegment -R 577 0 1010 250
+ \setchemicallinesegment +R 577 0 1010 -250
+ \setchemicallinesegment ER1 577 50 1077 50
+ \setchemicallinesegment ER2 577 -50 1077 -50
+ \setchemicallinesegment SR 837 0 1077 0
+ \setchemicallinesegment -SR 802 130 1010 250
+ \setchemicallinesegment +SR 802 -130 1010 -250
+ \setchemicallinesegment DR1 837 50 1077 50
+ \setchemicallinesegment DR2 837 -50 1077 -50
+ %
+ \setchemicaltextelement Z 577 0
+ \setchemicaltextelement RZ 1177 0
+ \setchemicaltextelement -RZ 1097 300
+ \setchemicaltextelement +RZ 1097 -300
+ \setchemicaltextelement CRZ 1077 0
+ %
+ \def\processchemical[##1##2##3##4]%
+ {\processaction
+ [##1##2##3##4]
+ [ ROT##4=>\processchemicalrotation{##4},
+ MOV##4=>\processchemicaltranslate{##4},
+ SUB##4=>\processchemicalsubstitute{##4},
+ ADJ##4=>\processchemicaldistance{##4},
+ -RZ##4=>\processchemicaltextelement{-RZ}{##4}{#1}{3}
+ {l,t,r, l,r,l, r,b,l, r,l,r},
+ +RZ##4=>\processchemicaltextelement{+RZ}{##4}{#1}{3}
+ {l,r,b, r,r,l, r,l,t, l,l,r},
+ -SB##4=>\processchemicallinesegment{-SB}{##4},
+ +SB##4=>\processchemicallinesegment{+SB}{##4},
+ -SR##4=>\processchemicallinesegment{-SR}{##4},
+ +SR##4=>\processchemicallinesegment{+SR}{##4},
+ CRZ##4=>\processchemicaltextelement{CRZ}{##4}{#1}{0}
+ {},
+ DB##3##4=>\processchemicallinesegment{DB}{##3##4},
+ DR##3##4=>\processchemicallinesegment{DR}{##3##4},
+ RZ##3##4=>\processchemicaltextelement{RZ}{##3##4}{#1}{3}
+ {l,r,r, t,r,l, r,l,l, b,l,r},
+ ER##3##4=>\processchemicallinesegment{ER}{##3##4},
+ SB##3##4=>\processchemicallinesegment{SB}{##3##4},
+ SR##3##4=>\processchemicallinesegment{SR}{##3##4},
+ -R##3##4=>\processchemicallinesegment{-R}{##3##4},
+ +R##3##4=>\processchemicallinesegment{+R}{##3##4},
+ B##2##3##4=>\processchemicallinesegment{B}{##2##3##4},
+ R##2##3##4=>\processchemicallinesegment{R}{##2##3##4},
+ Z##2##3##4=>\processchemicaltextelement{Z}{##2##3##4}{#1}{0}{},
+ \s!unknown=>\unknownchemical{##1##2##3##4}]}}
+
+\def\executechemicalFOUR[#1]%
+ {\setchemicalname FOUR
+ %
+ \setchemicalmaximum 4
+ \setchemicaldistance 500
+ \setchemicalsubstitute 0
+ %
+ \setchemicalrotation 1 1 0 0 -1 -1 0 0 1
+ \setchemicalrotation 2 0 -1 -1 0 0 1 1 0
+ \setchemicalrotation 3 -1 0 0 1 1 0 0 -1
+ \setchemicalrotation 4 0 1 1 0 0 -1 -1 0
+ %
+ \setchemicalangle 1 0 90 180 270
+ \setchemicalangle 2 90 180 270 0
+ \setchemicalangle 3 180 270 0 90
+ \setchemicalangle 4 270 0 90 180
+ %
+ \setchemicaltranslate 1 -1000 0
+ \setchemicaltranslate 2 0 1000
+ \setchemicaltranslate 3 1000 0
+ \setchemicaltranslate 4 0 -1000
+ %
+ \setchemicallinesegment B 500 500 500 -500
+ \setchemicallinesegment SB 500 240 500 -240
+ \setchemicallinesegment -SB 500 240 500 -500
+ \setchemicallinesegment +SB 500 500 500 -240
+ \setchemicallinesegment DB1 450 240 450 -240
+ \setchemicallinesegment DB2 550 240 550 -240
+ \setchemicallinesegment EB 360 300 360 -300
+ \setchemicallinesegment R 500 500 854 854
+ \setchemicallinesegment -R 500 500 500 1000
+ \setchemicallinesegment +R 500 500 1000 500
+ \setchemicallinesegment ER1 465 535 819 889
+ \setchemicallinesegment ER2 535 465 889 819
+ \setchemicallinesegment SR 684 684 854 854
+ \setchemicallinesegment -SR 500 760 500 1000
+ \setchemicallinesegment +SR 760 500 1000 500
+ \setchemicallinesegment DR1 649 719 819 889
+ \setchemicallinesegment DR2 719 649 889 819
+ %
+ \setchemicaltextelement Z 500 500
+ \setchemicaltextelement RZ 925 925
+ \setchemicaltextelement -RZ 500 1100
+ \setchemicaltextelement +RZ 1100 500
+ \setchemicaltextelement CRZ 1038 1038
+ %
+ \setchemicaltextelement ZN 350 350
+ %
+ \def\processchemical[##1##2##3##4]%
+ {\processaction
+ [##1##2##3##4]
+ [ PB:##4=>\beginchemicalpicture{##4},
+ PE????=>\endchemicalpicture,
+ ROT##4=>\processchemicalrotation{##4},
+ SUB##4=>\processchemicalsubstitute{##4},
+ ADJ##4=>\processchemicaldistance{##4},
+ MOV##4=>\processchemicaltranslate{##4},
+ -RZ##4=>\processchemicaltextelement{-RZ}{##4}{#1}{4}
+ {b,l,t,r, l,t,r,b, t,r,b,l, r,b,l,t},
+ +RZ##4=>\processchemicaltextelement{+RZ}{##4}{#1}{4}
+ {l,t,r,b, t,r,b,l, r,b,l,t, b,l,t,r},
+ -SB##4=>\processchemicallinesegment{-SB}{##4},
+ +SB##4=>\processchemicallinesegment{+SB}{##4},
+ -SR##4=>\processchemicallinesegment{-SR}{##4},
+ +SR##4=>\processchemicallinesegment{+SR}{##4},
+ CRZ##4=>\processchemicaltextelement{CRZ}{##4}{#1}{0}{},
+ ZN##3##4=>\processchemicaltextconstant{ZN}{##3##4}{\chemicaltextelementnumber}{0},
+ ZT##3##4=>\processchemicaltextelement{ZN}{##3##4}{#1}{0}{},
+ DB##3##4=>\processchemicallinesegment{DB}{##3##4},
+ DR##3##4=>\processchemicallinesegment{DR}{##3##4},
+ EB##3##4=>\processchemicallinesegment{EB}{##3##4},
+ ER##3##4=>\processchemicallinesegment{ER}{##3##4},
+ RZ##3##4=>\processchemicaltextelement{RZ}{##3##4}{#1}{4}
+ {lb,lt,rt,rb, lt,rt,rb,lb, rt,rb,lb,lt, rb,lb,lt,rt},
+ SB##3##4=>\processchemicallinesegment{SB}{##3##4},
+ SR##3##4=>\processchemicallinesegment{SR}{##3##4},
+ -R##3##4=>\processchemicallinesegment{-R}{##3##4},
+ +R##3##4=>\processchemicallinesegment{+R}{##3##4},
+ B##2##3##4=>\processchemicallinesegment{B}{##2##3##4},
+ R##2##3##4=>\processchemicallinesegment{R}{##2##3##4},
+ Z##2##3##4=>\processchemicaltextelement{Z}{##2##3##4}{#1}{0}{},
+ \s!unknown=>\unknownchemical{##1##2##3##4}]}}
+
+\def\executechemicalFIVE[#1]%
+ {\setchemicalname FIVE
+ %
+ \setchemicalmaximum 5
+ \setchemicaldistance 688
+ \setchemicalsubstitute 1226
+ %
+ \setchemicalrotation 1 1 0 0 -1 -1 0 0 1
+ \setchemicalrotation 2 0.309 -0.951 -0.951 -0.309 -0.309 0.940 0.951 0.309
+ \setchemicalrotation 3 -0.809 -0.588 -0.588 0.809 0.809 0.588 0.588 -0.809
+ \setchemicalrotation 4 -0.809 0.588 0.588 0.809 0.809 -0.588 -0.588 -0.809
+ \setchemicalrotation 5 0.309 0.951 0.951 -0.309 -0.309 -0.951 -0.951 0.309
+ %
+ \setchemicalangle 1 0 90 180 270
+ \setchemicalangle 2 72 162 252 342
+ \setchemicalangle 3 144 234 324 54
+ \setchemicalangle 4 216 306 36 126
+ \setchemicalangle 5 288 18 108 198
+ %
+ \setchemicaltranslate 1 -1376 0
+ \setchemicaltranslate 2 -425 1304
+ \setchemicaltranslate 3 1113 809
+ \setchemicaltranslate 4 1113 -809
+ \setchemicaltranslate 5 -425 -1304
+ %
+ \setchemicallinesegment A 1188 500 1188 -500
+ \setchemicallinesegment B 688 500 688 -500
+ \setchemicallinesegment S -263 808 688 -500
+ \setchemicallinesegment SS -116 606 541 -298
+ \setchemicallinesegment -SS -263 808 541 -298
+ \setchemicallinesegment +SS -116 606 688 -500
+ \setchemicallinesegment SB 688 240 688 -240
+ \setchemicallinesegment -SB 688 240 688 -500
+ \setchemicallinesegment +SB 688 500 688 -240
+ \setchemicallinesegment DB1 638 240 638 -240
+ \setchemicallinesegment DB2 738 240 738 -240
+ \setchemicallinesegment EB 548 340 548 -340
+ \setchemicallinesegment R 688 500 1093 794
+ \setchemicallinesegment -R 688 500 688 1000
+ \setchemicallinesegment +R 688 500 1163 345
+ \setchemicallinesegment ER1 659 540 1064 834
+ \setchemicallinesegment ER2 727 460 1122 754
+ \setchemicallinesegment SR 898 653 1093 794
+ \setchemicallinesegment -SR 688 760 688 1000
+ \setchemicallinesegment +SR 935 420 1163 345
+ \setchemicallinesegment DR1 869 693 1064 834
+ \setchemicallinesegment DR2 927 613 1122 754
+ %
+ \setchemicaltextelement Z 688 500
+ \setchemicaltextelement RZ 1188 863
+ \setchemicaltextelement -RZ 688 1100
+ \setchemicaltextelement +RZ 1258 315
+ \setchemicaltextelement CRZ 1323 947
+ %
+ \setchemicalcircsegment C -36 36 590 72 475 -345
+ \setchemicalcircsegment CC -72 0 590 72 182 -561
+ %
+ \setchemicaltextelement ZN 468 350
+ \setchemicaltextelement RN 860 625 % 1.25 Z
+ \setchemicaltextelement RTN 785 728 % .12 / 103 75
+ \setchemicaltextelement RBN 935 522
+ %
+ \def\processchemical[##1##2##3##4]%
+ {\processaction
+ [##1##2##3##4]
+ [ FRONT????=>{\executechemicalFIVEFRONT[#1]},
+ PB:##4=>\beginchemicalpicture{##4},
+ PE????=>\endchemicalpicture,
+ ROT##4=>\processchemicalrotation{##4},
+ SUB##4=>\processchemicalsubstitute{##4},
+ ADJ##4=>\processchemicaldistance{##4},
+ MOV##4=>\processchemicaltranslate{##4},
+ -RZ##4=>\processchemicaltextelement{-RZ}{##4}{#1}{5}
+ {b,l,t,r,r, l,t,r,r,l, t,r,r,l,l, r,b,l,t,r},
+ +RZ##4=>\processchemicaltextelement{+RZ}{##4}{#1}{5}
+ {l,t,r,r,b, t,r,r,l,l, r,r,l,l,r, b,l,l,r,r},
+ -SB##4=>\processchemicallinesegment{-SB}{##4},
+ +SB##4=>\processchemicallinesegment{+SB}{##4},
+ -SR##4=>\processchemicallinesegment{-SR}{##4},
+ +SR##4=>\processchemicallinesegment{+SR}{##4},
+ -RD##4=>\processchemicaldashedlinesegment{-R}{##4},
+ +RD##4=>\processchemicaldashedlinesegment{+R}{##4},
+ -RB##4=>\processchemicaldeltalinesegment{-R}{##4},
+ +RB##4=>\processchemicaldeltalinesegment{+R}{##4},
+ CRZ##4=>\processchemicaltextelement{CRZ}{##4}{#1}{0}{},
+ RTN##4=>\processchemicaltextconstant{RTN}{##4}{\chemicaltextelementnumber}{0},
+ RTT##4=>\processchemicaltextelement{RTN}{##4}{#1}{0}{},
+ RBN##4=>\processchemicaltextconstant{RBN}{##4}{\chemicaltextelementnumber}{0},
+ RBT##4=>\processchemicaltextelement{RBN}{##4}{#1}{0}{},
+ -SS##4=>\processchemicallinesegment{-SS}{##4},
+ +SS##4=>\processchemicallinesegment{+SS}{##4},
+ CCD##4=>\processchemicaldottsegment{CC}{##4},
+ SS##3##4=>\processchemicallinesegment{SS}{##3##4},
+ RD##3##4=>\processchemicaldashedlinesegment{R}{##3##4},
+ RB##3##4=>\processchemicaldeltalinesegment{R}{##3##4},
+ ZN##3##4=>\processchemicaltextconstant{ZN}{##3##4}{\chemicaltextelementnumber}{0},
+ ZT##3##4=>\processchemicaltextelement{ZN}{##3##4}{#1}{0}{},
+ RN##3##4=>\processchemicaltextconstant{RN}{##3##4}{\chemicaltextelementnumber}{0},
+ RT##3##4=>\processchemicaltextelement{RN}{##3##4}{#1}{0}{},
+ AU##3##4=>\processchemicaluparrowsegment{A}{##3##4},
+ AD##3##4=>\processchemicaldownarrowsegment{A}{##3##4},
+ CC##3##4=>\processchemicalcircsegment{CC}{##3##4},
+ CD##3##4=>\processchemicaldottsegment{C}{##3##4},
+ DB##3##4=>\processchemicallinesegment{DB}{##3##4},
+ DR##3##4=>\processchemicallinesegment{DR}{##3##4},
+ EB##3##4=>\processchemicallinesegment{EB}{##3##4},
+ ER##3##4=>\processchemicallinesegment{ER}{##3##4},
+ RZ##3##4=>\processchemicaltextelement{RZ}{##3##4}{#1}{5}
+ {l,l,r,r,r, l,r,r,b,l, r,r,b,l,t, r,l,l,t,r},
+ SB##3##4=>\processchemicallinesegment{SB}{##3##4},
+ SR##3##4=>\processchemicallinesegment{SR}{##3##4},
+ -R##3##4=>\processchemicallinesegment{-R}{##3##4},
+ +R##3##4=>\processchemicallinesegment{+R}{##3##4},
+ B##2##3##4=>\processchemicallinesegment{B}{##2##3##4},
+ C##2##3##4=>\processchemicalcircsegment{C}{##2##3##4},
+ R##2##3##4=>\processchemicallinesegment{R}{##2##3##4},
+ S##2##3##4=>\processchemicallinesegment{S}{##2##3##4},
+ Z##2##3##4=>\processchemicaltextelement{Z}{##2##3##4}{#1}{0}{},
+ \s!unknown=>\unknownchemical{##1##2##3##4}]}}
+
+\def\executechemicalSIX[#1]%
+ {\setchemicalname SIX
+ %
+ \setchemicalmaximum 6
+ \setchemicalsubstitute 1375
+ \setchemicaldistance 866
+ %
+ \setchemicalrotation 1 1 0 0 -1 -1 0 0 1
+ \setchemicalrotation 2 0.5 -0.866 -0.866 -0.5 -0.5 0.866 0.866 0.5
+ \setchemicalrotation 3 -0.5 -0.866 -0.866 0.5 0.5 0.866 0.866 -0.5
+ \setchemicalrotation 4 -1 0 0 1 1 0 0 -1
+ \setchemicalrotation 5 -0.5 0.866 0.866 0.5 0.5 -0.866 -0.866 -0.5
+ \setchemicalrotation 6 0.5 0.866 0.866 -0.5 -0.5 -0.866 -0.866 0.5
+ %
+ \setchemicalangle 1 0 90 180 270
+ \setchemicalangle 2 60 150 240 330
+ \setchemicalangle 3 120 210 300 30
+ \setchemicalangle 4 180 270 0 90
+ \setchemicalangle 5 240 330 60 150
+ \setchemicalangle 6 300 30 120 210
+ %
+ \setchemicaltranslate 1 -1732 0
+ \setchemicaltranslate 2 -866 1500
+ \setchemicaltranslate 3 866 1500
+ \setchemicaltranslate 4 1732 0
+ \setchemicaltranslate 5 866 -1500
+ \setchemicaltranslate 6 -866 -1500
+ %
+ \setchemicallinesegment A 1386 500 1386 -500
+ \setchemicallinesegment S 0 1000 866 -500
+ \setchemicallinesegment SS 125 783 741 -283
+ \setchemicallinesegment -SS 0 1000 741 -283
+ \setchemicallinesegment +SS 125 783 866 -500
+ \setchemicallinesegment B 866 500 866 -500
+ \setchemicallinesegment SB 866 240 866 -240
+ \setchemicallinesegment -SB 866 240 866 -500
+ \setchemicallinesegment +SB 866 500 866 -240
+ \setchemicallinesegment DB1 816 240 816 -240
+ \setchemicallinesegment DB2 916 240 916 -240
+ \setchemicallinesegment EB 726 340 726 -340
+ \setchemicallinesegment R 866 500 1299 750
+ \setchemicallinesegment -R 866 500 866 1000
+ \setchemicallinesegment +R 866 500 1299 250
+ \setchemicallinesegment ER1 841 543 1274 793
+ \setchemicallinesegment ER2 891 457 1324 707
+ \setchemicallinesegment SR 1091 630 1299 750
+ \setchemicallinesegment -SR 866 740 866 1000
+ \setchemicallinesegment +SR 1091 370 1299 250
+ \setchemicallinesegment DR1 1066 673 1274 793
+ \setchemicallinesegment DR2 1116 588 1324 707
+ \setchemicallinesegment MID1 0 1000 -150 200
+ \setchemicallinesegment MID2 0 -1000 -150 -200
+ \setchemicallinesegment MIDS1 0 1000 -180 0
+ \setchemicallinesegment MIDS2 0 -1000 -180 0
+ %
+ \setchemicalcircsegment C -30 30 700 60 600 -346
+ \setchemicalcircsegment CC -60 0 700 60 350 -606
+ %
+ \setchemicaltextelement Z 866 500
+ \setchemicaltextelement RZ 1386 800
+ \setchemicaltextelement -RZ 866 1100
+ \setchemicaltextelement +RZ 1386 200
+ \setchemicaltextelement CRZ 1524 880
+ \setchemicaltextelement MIDZ -150 0
+ %
+ \setchemicaltextelement ZN 589 350
+ \setchemicaltextelement RN 1083 625 % 1.25 Z
+ \setchemicaltextelement RTN 1008 755 % .12 / 130 75
+ \setchemicaltextelement RBN 1158 495
+ %
+ \def\processchemical[##1##2##3##4##5]%
+ {\processaction
+ [##1##2##3##4##5]
+ [ FRONT????=>{\executechemicalSIXFRONT[#1]},
+ MID????=>\processchemicallinesegment{MID}{1????},
+ MIDS????=>\processchemicallinesegment{MIDS}{1????},
+ MIDZ????=>\processchemicaltextelement{MIDZ}{1????}{#1}{0}{},
+ PB:##4##5=>\beginchemicalpicture{##4##5},
+ PE????=>\endchemicalpicture,
+ ROT##4##5=>\processchemicalrotation{##4##5},
+ SUB##4##5=>\processchemicalsubstitute{##4##5},
+ ADJ##4##5=>\processchemicaldistance{##4##5},
+ MOV##4##5=>\processchemicaltranslate{##4##5},
+ -RZ##4##5=>\processchemicaltextelement{-RZ}{##4##5}{#1}{6}
+ {b,l,l,t,r,r, l,l,r,r,r,l, t,r,r,b,l,l, r,r,l,l,l,r},
+ +RZ##4##5=>\processchemicaltextelement{+RZ}{##4##5}{#1}{6}
+ {l,t,r,r,b,l, r,r,r,l,l,l, r,b,l,l,t,r, l,l,l,r,r,r},
+ -SB##4##5=>\processchemicallinesegment{-SB}{##4##5},
+ +SB##4##5=>\processchemicallinesegment{+SB}{##4##5},
+ -SR##4##5=>\processchemicallinesegment{-SR}{##4##5},
+ +SR##4##5=>\processchemicallinesegment{+SR}{##4##5},
+ -RD##4##5=>\processchemicaldashedlinesegment{-R}{##4##5},
+ +RD##4##5=>\processchemicaldashedlinesegment{+R}{##4##5},
+ -RB##4##5=>\processchemicaldeltalinesegment{-R}{##4##5},
+ +RB##4##5=>\processchemicaldeltalinesegment{+R}{##4##5},
+ CRZ##4##5=>\processchemicaltextelement{CRZ}{##4##5}{#1}{0}{},
+ -SS##4##5=>\processchemicallinesegment{-SS}{##4##5},
+ +SS##4##5=>\processchemicallinesegment{+SS}{##4##5},
+ CCD##4##5=>\processchemicaldottsegment{CC}{##4##5},
+RTN##4##5=>\processchemicaltextconstant{RTN}{##4##5}{\chemicaltextelementnumber}{0},
+RTT##4##5=>\processchemicaltextelement{RTN}{##4##5}{#1}{0}{},
+RBN##4##5=>\processchemicaltextconstant{RBN}{##4##5}{\chemicaltextelementnumber}{0},
+RBT##4##5=>\processchemicaltextelement{RBN}{##4##5}{#1}{0}{},
+ SS##3##4##5=>\processchemicallinesegment{SS}{##3##4##5},
+ RD##3##4##5=>\processchemicaldashedlinesegment{R}{##3##4##5},
+ RB##3##4##5=>\processchemicaldeltalinesegment{R}{##3##4##5},
+ ZN##3##4##5=>\processchemicaltextconstant{ZN}{##3##4##5}{\chemicaltextelementnumber}{0},
+ ZT##3##4##5=>\processchemicaltextelement{ZN}{##3##4##5}{#1}{0}{},
+RN##3##4##5=>\processchemicaltextconstant{RN}{##3##4##5}{\chemicaltextelementnumber}{0},
+RT##3##4##5=>\processchemicaltextelement{RN}{##3##4##5}{#1}{0}{},
+ AU##3##4##5=>\processchemicaluparrowsegment{A}{##3##4##5},
+ AD##3##4##5=>\processchemicaldownarrowsegment{A}{##3##4##5},
+ CD##3##4##5=>\processchemicaldottsegment{C}{##3##4##5},
+ CC##3##4##5=>\processchemicalcircsegment{CC}{##3##4##5},
+ DB##3##4##5=>\processchemicallinesegment{DB}{##3##4##5},
+ EB##3##4##5=>\processchemicallinesegment{EB}{##3##4##5},
+ ER##3##4##5=>\processchemicallinesegment{ER}{##3##4##5},
+ RZ##3##4##5=>\processchemicaltextelement{RZ}{##3##4##5}{#1}{6}
+ {l,l,t,r,r,b, l,r,r,r,l,l, r,r,b,l,l,t, r,l,l,l,r,r},
+ SB##3##4##5=>\processchemicallinesegment{SB}{##3##4##5},
+ SR##3##4##5=>\processchemicallinesegment{SR}{##3##4##5},
+ DR##3##4##5=>\processchemicallinesegment{DR}{##3##4##5},
+ -R##3##4##5=>\processchemicallinesegment{-R}{##3##4##5},
+ +R##3##4##5=>\processchemicallinesegment{+R}{##3##4##5},
+ B##2##3##4##5=>\processchemicallinesegment{B}{##2##3##4##5},
+ C##2##3##4##5=>\processchemicalcircsegment{C}{##2##3##4##5},
+ R##2##3##4##5=>\processchemicallinesegment{R}{##2##3##4##5},
+ S##2##3##4##5=>\processchemicallinesegment{S}{##2##3##4##5},
+ Z##2##3##4##5=>\processchemicaltextelement{Z}{##2##3##4##5}{#1}{0}{},
+ \s!unknown=>\unknownchemical{##1##2##3##4##5}]}}
+
+\def\executechemicalSEVEN[#1]% incomplete !
+ {\setchemicalname SEVEN
+ %
+ \setchemicalmaximum 7
+ \setchemicalsubstitute -
+ \setchemicaldistance 1038
+ %
+ \setchemicalrotation 1 .623 .782 - - - - - -
+ \setchemicalrotation 2 -.223 .975 - - - - - -
+ \setchemicalrotation 3 -.901 .434 - - - - - -
+ \setchemicalrotation 4 -.901 -.434 - - - - - -
+ \setchemicalrotation 5 -.223 -.975 - - - - - -
+ \setchemicalrotation 6 .623 -.782 - - - - - -
+ \setchemicalrotation 7 1 0 - - - - - -
+ %
+ \setchemicalangle 1 0 - - -
+ \setchemicalangle 2 51.429 - - -
+ \setchemicalangle 3 102.857 - - -
+ \setchemicalangle 4 154.286 - - -
+ \setchemicalangle 5 205.714 - - -
+ \setchemicalangle 6 257.143 - - -
+ \setchemicalangle 7 308.571 - - -
+ %
+ \setchemicaltranslate 1 - -
+ \setchemicaltranslate 2 - -
+ \setchemicaltranslate 3 - -
+ \setchemicaltranslate 4 - -
+ \setchemicaltranslate 5 - -
+ \setchemicaltranslate 6 - -
+ \setchemicaltranslate 7 - -
+ %
+ \setchemicallinesegment B 1038 500 1038 -500
+ \setchemicallinesegment SB 1038 240 1038 -240
+ \setchemicallinesegment -SB 1038 240 1038 -500
+ \setchemicallinesegment +SB 1038 500 1038 -240
+ %
+ \setchemicaltextelement Z 1038 500
+ %
+ \def\processchemical[##1##2##3##4##5]%
+ {\processaction
+ [##1##2##3##4##5]
+ [ PB:##4##5=>\beginchemicalpicture{##4##5},
+ PE????=>\endchemicalpicture,
+ %ROT##4##5=>\processchemicalrotation{##4},
+ %SUB##4##5=>\processchemicalsubstitute{##4##5},
+ %ADJ##4##5=>\processchemicaldistance{##4##5},
+ %MOV##4##5=>\processchemicaltranslate{##4##5},
+ -SB##4##5=>\processchemicallinesegment{-SB}{##4##5},
+ +SB##4##5=>\processchemicallinesegment{+SB}{##4##5},
+ SB##3##4##5=>\processchemicallinesegment{SB}{##3##4##5},
+ B##2##3##4##5=>\processchemicallinesegment{B}{##2##3##4##5},
+ Z##2##3##4##5=>\processchemicaltextelement{Z}{##2##3##4##5}{#1}{0}{},
+ \s!unknown=>\unknownchemical{##1##2##3##4##5}]}}
+
+\def\executechemicalEIGHT[#1]% incomplete !
+ {\setchemicalname EIGHT
+ %
+ \setchemicalmaximum 8
+ %\setchemicalsubstitute 1307
+ \setchemicaldistance 1207
+ %
+ \setchemicalrotation 1 .707 .707 - - - - - -
+ \setchemicalrotation 2 0 1 - - - - - -
+ \setchemicalrotation 3 -.707 .707 - - - - - -
+ \setchemicalrotation 4 -1 0 - - - - - -
+ \setchemicalrotation 5 -.707 -.707 - - - - - -
+ \setchemicalrotation 6 0 -1 - - - - - -
+ \setchemicalrotation 7 .707 -.707 - - - - - -
+ \setchemicalrotation 8 1 0 - - - - - -
+ %
+ \setchemicalangle 1 45 - - -
+ \setchemicalangle 2 90 - - -
+ \setchemicalangle 3 135 - - -
+ \setchemicalangle 4 180 - - -
+ \setchemicalangle 5 225 - - -
+ \setchemicalangle 6 270 - - -
+ \setchemicalangle 7 315 - - -
+ \setchemicalangle 8 0 - - -
+ %
+ \setchemicaltranslate 1 -2414 0
+ \setchemicaltranslate 2 -1706 1706
+ \setchemicaltranslate 3 0 2414
+ \setchemicaltranslate 4 1706 1706
+ \setchemicaltranslate 5 2414 0
+ \setchemicaltranslate 6 1706 -1706
+ \setchemicaltranslate 7 0 -2414
+ \setchemicaltranslate 8 -1706 -1706
+ %
+ \setchemicallinesegment B 1207 500 1207 -500
+ \setchemicallinesegment SB 1207 240 1207 -240
+ \setchemicallinesegment -SB 1207 240 1207 -500
+ \setchemicallinesegment +SB 1207 500 1207 -240
+ %
+ \setchemicaltextelement Z 1207 500
+ %
+ \def\processchemical[##1##2##3##4##5]%
+ {\processaction
+ [##1##2##3##4##5]
+ [ PB:##4##5=>\beginchemicalpicture{##4##5},
+ PE????=>\endchemicalpicture,
+ %SUB##4##5=>\processchemicalsubstitute{##4##5},
+ ADJ##4##5=>\processchemicaldistance{##4##5},
+ MOV##4##5=>\processchemicaltranslate{##4##5},
+ -SB##4##5=>\processchemicallinesegment{-SB}{##4##5},
+ +SB##4##5=>\processchemicallinesegment{+SB}{##4##5},
+ SB##3##4##5=>\processchemicallinesegment{SB}{##3##4##5},
+ B##2##3##4##5=>\processchemicallinesegment{B}{##2##3##4##5},
+ Z##2##3##4##5=>\processchemicaltextelement{Z}{##2##3##4##5}{#1}{0}{},
+ \s!unknown=>\unknownchemical{##1##2##3##4##5}]}}
+
+\def\executechemicalFIVEFRONT[#1]%
+ {\executechemicalFIVE[]%
+ %
+ \setchemicalname FIVEFRONT
+ %
+ \setchemicallinesegment -R 688 500 688 100
+ \setchemicallinesegment +R 688 500 688 900
+ %
+ \setchemicaltextelement -RZ 0 -1300
+ \setchemicaltextelement +RZ 0 1300
+ %
+ \def\processchemical[##1##2##3##4]%
+ {\def\chemicalrotation{2}%
+ \processaction
+ [##1##2##3##4]
+ [ -RZ##4=>\processchemicalunrotatedtextelement{Z}{-RZ}{##4}{#1}{5}
+ {,,,,, t,t,t,t,t},
+ +RZ##4=>\processchemicalunrotatedtextelement{Z}{+RZ}{##4}{#1}{5}
+ {,,,,, b,b,b,b,b},
+ -SB##4=>\processchemicallinesegment{-SB}{##4},
+ +SB##4=>\processchemicallinesegment{+SB}{##4},
+ SB##3##4=>\processchemicallinesegment{SB}{##3##4},
+ -R##3##4=>\processchemicalunrotatedlinesegment{t}{-R}{##3##4},
+ +R##3##4=>\processchemicalunrotatedlinesegment{b}{+R}{##3##4},
+ BB##3##4=>\processchemicalzlinesegment{B}{##3##4},
+ R##2##3##4=>\processchemicalunrotatedlinesegment{t}{-R}{##2##3##4}%
+ \processchemicalunrotatedlinesegment{b}{+R}{##2##3##4},
+ B##2##3##4=>\processchemicallinesegment{B}{##2##3##4},
+ Z##2##3##4=>\processchemicaltextelement{Z}{##2##3##4}{#1}{0}{},
+ \s!unknown=>\unknownchemical{##1##2##3##4}]}}
+
+\def\executechemicalSIXFRONT[#1]%
+ {\executechemicalSIX[]%
+ %
+ \setchemicalname SIXFRONT
+ %
+ \setchemicallinesegment -R 866 500 866 100
+ \setchemicallinesegment +R 866 500 866 900
+ %
+ \setchemicaltextelement -RZ 0 -1300
+ \setchemicaltextelement +RZ 0 1300
+ %
+ \def\processchemical[##1##2##3##4]%
+ {\def\chemicalrotation{2}%
+ \processaction
+ [##1##2##3##4]
+ [ -RZ##4=>\processchemicalunrotatedtextelement{Z}{-RZ}{##4}{#1}{6}
+ {,,,,,, t,t,t,t,t,t},
+ +RZ##4=>\processchemicalunrotatedtextelement{Z}{+RZ}{##4}{#1}{6}
+ {,,,,,, b,b,b,b,b,b},
+ -SB##4=>\processchemicallinesegment{-SB}{##4},
+ +SB##4=>\processchemicallinesegment{+SB}{##4},
+ SB##3##4=>\processchemicallinesegment{SB}{##3##4},
+ -R##3##4=>\processchemicalunrotatedlinesegment{t}{-R}{##3##4},
+ +R##3##4=>\processchemicalunrotatedlinesegment{b}{+R}{##3##4},
+ BB##3##4=>\processchemicalzlinesegment{B}{##3##4},
+ R##2##3##4=>\processchemicalunrotatedlinesegment{t}{-R}{##2##3##4}%
+ \processchemicalunrotatedlinesegment{b}{+R}{##2##3##4},
+ B##2##3##4=>\processchemicallinesegment{B}{##2##3##4},
+ Z##2##3##4=>\processchemicaltextelement{Z}{##2##3##4}{#1}{0}{},
+ \s!unknown=>\unknownchemical{##1##2##3##4}]}}
+
+% 1 : 0
+% 2 : -115
+% 3* : -195
+% 3 : -165
+% 4 : -245
+
+\def\executechemicalCARBON[#1]%
+ {\setchemicalname CARBON
+ %
+ \setchemicalmaximum 4
+ \setchemicaldistance 0
+ \setchemicalsubstitute 0
+ %
+ \setchemicalrotation 1 1 0 0 -1 -1 0 0 1
+ \setchemicalrotation 2 -0.423 -0.906 -0.906 0.423 0.423 0.906 0.906 -0.423
+ \setchemicalrotation 3 -0.966 -0.259 -0.259 0.966 0.966 0.259 0.259 -0.966
+ \setchemicalrotation 3* -0.966 0.259 0.259 0.966 0.966 -0.259 -0.259 -0.966
+ \setchemicalrotation 4 -0.423 0.906 0.906 0.423 0.423 -0.906 -0.906 -0.423
+ %
+ \setchemicalangle 1 0 90 180 270
+ \setchemicalangle 2 115 205 295 25
+ \setchemicalangle 3 165 255 345 75
+ \setchemicalangle 3* 195 285 15 105
+ \setchemicalangle 4 245 335 65 155
+ %
+ \setchemicaltranslate 1 -1500 0
+ \setchemicaltranslate 2 0 1500
+ \setchemicaltranslate 3 1500 0
+ \setchemicaltranslate 4 0 -1500
+ %
+ \setchemicallinesegment B1 500 0 1000 0
+ \setchemicallinesegment B2 300 0 1000 0
+ \setchemicallinesegment B3 500 0 1000 0
+ \setchemicallinesegment B4 300 0 1000 0
+ %
+ \setchemicaltextelement Z 1100 0
+ %
+ \setchemicalcircsegment C 0 360 500 360 0 -500
+ %
+ \def\processchemical[##1##2##3##4##5]%
+ {\processaction
+ [##1##2##3##4##5]
+ [ MIR????=>\setchemicalmirror{3},
+ -MIR????=>\resetchemicalmirror{3},
+ *MIR????=>\togglechemicalmirror{3},
+ CB????=>\processlocalchemicals{B,C,Z},
+ C????=>\processchemicalcircsegment{C}{1????},
+ -ROT##5=>\reversechemical{ROT}{##5}{3,4,1,2},
+ ROT##4##5=>\processchemicalrotation{##4##5},
+ MOV##4##5=>\processchemicaltranslate{##4##5},
+ CB##3##4##5=>\processlocalchemicals
+ {ROT##3,C,B,Z2..4,
+ MOV##3,*MIR,-ROT##3,C,B,Z2..4},
+ B##2##3##4##5=>\processprivatechemicallinesegment{B}{##2##3##4##5},
+ Z##2##3##4##5=>\processchemicaltextelement{Z}{##2##3##4##5}{#1}{4}
+ {l,t,r,b, t,r,b,l, r,b,l,t, b,l,t,r},
+ \s!unknown=>\unknownchemical{##1##2##3##4##5}]}}
+
+% 1: 45 2: -90 3: -225
+% 4: -45 5: -135 6: -270
+
+\newif\ifNEWMANstagger \NEWMANstaggertrue
+
+\def\executechemicalNEWMANSTAGGER%
+ {\NEWMANstaggertrue\executechemicalNEWMAN}
+
+\def\executechemicalNEWMANECLIPSE%
+ {\NEWMANstaggerfalse\executechemicalNEWMAN}
+
+\def\executechemicalNEWMAN[#1]%
+ {\setchemicalname NEWMAN
+ %
+ \setchemicalmaximum 6
+ \setchemicaldistance 0
+ \setchemicalsubstitute 0
+ %
+ \ifNEWMANstagger
+ \setchemicalrotation 1 0.707 0.707 0.707 -0.707 -0.707 -0.707 -0.707 0.707
+ \setchemicalrotation 2 0 -1 -1 0 0 1 1 0
+ \setchemicalrotation 3 -0.707 0.707 0.707 0.707 0.707 -0.707 -0.707 -0.707
+ \else
+ \setchemicalrotation 1 .866 -.5 -.5 -.866 -.866 .5 .5 .866
+ \setchemicalrotation 2 -.259 .966 .966 .259 .259 -.966 -.966 -.259
+ \setchemicalrotation 3 -.5 -.866 -.866 .5 .5 .866 .866 -.5
+ \fi
+ \setchemicalrotation 4 0.707 -0.707 -0.707 -0.707 -0.707 0.707 0.707 0.707
+ \setchemicalrotation 5 -0.707 -0.707 -0.707 0.707 0.707 0.707 0.707 -0.707
+ \setchemicalrotation 6 0 1 1 0 0 -1 -1 0
+ %
+ \ifNEWMANstagger
+ \setchemicalangle 1 315 45 135 225
+ \setchemicalangle 2 90 180 270 0
+ \setchemicalangle 3 225 315 45 135
+ \else
+ \setchemicalangle 1 30 120 210 300
+ \setchemicalangle 2 255 345 75 165
+ \setchemicalangle 3 120 210 300 30
+ \fi
+ \setchemicalangle 4 45 135 225 315
+ \setchemicalangle 5 135 225 315 45
+ \setchemicalangle 6 270 0 90 180
+ %
+ \setchemicaltranslate 1 -1500 0
+ \setchemicaltranslate 2 0 1500
+ \setchemicaltranslate 3 1500 0
+ \setchemicaltranslate 4 0 -1500
+ %
+ \setchemicallinesegment B1 0 0 1000 0
+ \setchemicallinesegment B2 0 0 1000 0
+ \setchemicallinesegment B3 0 0 1000 0
+ \setchemicallinesegment B4 500 0 1000 0
+ \setchemicallinesegment B5 500 0 1000 0
+ \setchemicallinesegment B6 500 0 1000 0
+ %
+ \setchemicaltextelement Z 1100 0
+ %
+ \setchemicalcircsegment C 0 360 500 360 0 -500
+ %
+ \def\processchemical[##1##2##3##4]%
+ {\processaction
+ [##1##2##3##4]
+ [STAGGER????=>{\executechemicalNEWMANSTAGGER[#1]},
+ ECLIPSE????=>{\executechemicalNEWMANECLIPSE[#1]},
+ B????=>\processlocalchemicals{B1..6},
+ CB????=>\processlocalchemicals{B1..6,C,Z1..6},
+ C????=>\processchemicalcircsegment{C}{1????},
+ ROT##4=>\processchemicalrotation{##4},
+ MOV##4=>\processchemicaltranslate{##4},
+ B##2##3##4=>\processprivatechemicallinesegment{B}{##2##3##4},
+ Z##2##3##4=>\ifNEWMANstagger
+ \processchemicaltextelement{Z}{##2##3##4}{#1}{6}
+ {l,t,r,l,r,b, l,r,l,r,r,l, r,b,l,r,l,t, r,l,r,l,l,r}%
+ \else
+ \processchemicaltextelement{Z}{##2##3##4}{#1}{6}
+ {l,r,t,t,r,b, t,b,r,r,b,l, r,l,b,b,l,t, b,t,l,l,t,r}%
+ \fi,
+ \s!unknown=>\unknownchemical{##1##2##3##4}]}}
+
+\def\executechemicalCHAIR[#1]% smaller
+ {\setchemicalname CHAIR
+ %
+ \setchemicalmaximum 6
+ %
+ \setchemicallinesegment B1 1600 800 2800 -800
+ \setchemicallinesegment B2 2800 -800 800 0
+ \setchemicallinesegment B3 800 0 -1600 -800
+ \setchemicallinesegment B4 -1600 -800 -2800 800
+ \setchemicallinesegment B5 -2800 800 -800 0
+ \setchemicallinesegment B6 -800 0 1600 800
+ %
+ \setchemicallinesegment +R1 1600 800 1600 1600
+ \setchemicallinesegment +R2 2800 -800 2800 -1600
+ \setchemicallinesegment +R3 800 0 800 800
+ \setchemicallinesegment +R4 -1600 -800 -1600 -1600
+ \setchemicallinesegment +R5 -2800 800 -2800 1600
+ \setchemicallinesegment +R6 -800 0 -800 -800
+ %
+ \setchemicallinesegment -R1 1600 800 2350 522 % 750 278
+ \setchemicallinesegment -R2 2800 -800 3493 -400
+ \setchemicallinesegment -R3 800 0 1329 -600 % 528 600
+ \setchemicallinesegment -R4 -1600 -800 -2350 -522 % 750 278
+ \setchemicallinesegment -R5 -2800 800 -3493 400
+ \setchemicallinesegment -R6 -800 0 -1329 600 % 528 600
+ %
+ \setchemicaltextelement +RZ1 1600 1800
+ \setchemicaltextelement +RZ2 2800 -1800
+ \setchemicaltextelement +RZ3 800 1000
+ \setchemicaltextelement +RZ4 -1600 -1800
+ \setchemicaltextelement +RZ5 -2800 1800
+ \setchemicaltextelement +RZ6 -800 -1000
+ %
+ \setchemicaltextelement -RZ1 2538 453 % 200 lang
+ \setchemicaltextelement -RZ2 3666 -300
+ \setchemicaltextelement -RZ3 1460 -750
+ \setchemicaltextelement -RZ4 -2538 -453
+ \setchemicaltextelement -RZ5 -3666 300
+ \setchemicaltextelement -RZ6 -1460 750
+ %
+ \def\processchemical[##1##2##3##4##5]%
+ {\def\chemicalrotation{1}%
+ \processaction
+ [##1##2##3##4##5]
+ [ B????=>\processlocalchemicals{B1,B2,B3,B4,B5,B6},
+ -R????=>\processlocalchemicals{-R1,-R2,-R3,-R4,-R5,-R6},
+ +R????=>\processlocalchemicals{+R1,+R2,+R3,+R4,+R5,+R6},
+ B##2????=>{\getchemicallinesegment[0][B##2]},
+ -RZ##4????=>{\getchemicalfixedtextelement[-RZ##4][1][##4][l,l,tc,r,r,bc][#1]},
+ +RZ##4????=>{\getchemicalfixedtextelement[+RZ##4][1][##4][c][#1]},
+ -R##3????=>{\getchemicallinesegment[0][-R##3]},
+ +R##3????=>{\getchemicallinesegment[0][+R##3]},
+ \s!unknown=>\unknownchemical{##1##2##3##4##5}]}}
+
+\def\executechemicalarrow#1#2[#3]%
+ {\dogetcommalistelement1\from#3\to\toptext
+ \dogetcommalistelement2\from#3\to\bottext
+ \def\dochemicaltext##1%
+ {\dosetsubscripts%
+ $\@@dochemicalstyle{\@@localchemicalformat\strut##1}$%
+ \doresetsubscripts}%
+ \doifelse\@@chemicallocation\v!intext
+ {#1{\dochemicaltext\toptext}}%
+ {\setbox\chemicalsymbols=\hbox
+ {\box\chemicalsymbols
+ \vbox{\halign{##\cr
+ \hbox to 3em{\hss\dochemicaltext{\toptext}\hss}\cr
+ #2%
+ \hbox to 3em{\hss\dochemicaltext{\bottext}\hss}\cr}}}}}
+
+\def\executechemicalGIVES
+ {\executechemicalarrow
+ {\chemicalsinglepicturearrow}% nodig
+ {\rightarrowfill\cr}}
+
+\def\executechemicalEQUILIBRIUM
+ {\executechemicalarrow
+ {\chemicaldoublepicturearrow}% nodig
+ {\rightarrowfill\cr\leftarrowfill\cr}}
+
+\def\executechemicalMESOMERIC
+ {\executechemicalarrow
+ {\chemicalsinglepicturearrow}% nodig
+ {$\leftarrow\hskip-1em$\rightarrowfill\cr}}
+
+\def\executechemicalsign#1[#2]%
+ {\doifelse\@@chemicallocation\v!intext
+ {\dosetsubscripts
+ $\@@dochemicalstyle{\@@localchemicalformat#1}$%
+ \doresetsubscripts}
+ {\setbox\chemicalsymbols\hbox
+ {\box\chemicalsymbols
+ \dosetsubscripts
+ $\@@dochemicalstyle{\@@localchemicalformat#1}$%
+ \doresetsubscripts}}}
+
+\def\executechemicalPLUS
+ {\executechemicalsign{+}}
+
+\def\executechemicalMINUS
+ {\executechemicalsign{-}}
+
+\def\executechemicalEQUAL
+ {\executechemicalsign{=}}
+
+\def\executechemicalSPACE[#1]%
+ {\doifnot\@@chemicallocation\v!intext
+ {\setbox\chemicalsymbols\hbox
+ {\box\chemicalsymbols
+ \quad}}}
+
+\def\executechemicalCHEM[#1]%
+ {\doifnot\@@chemicallocation\v!intext
+ {\setbox\chemicalsymbols\hbox
+ {\box\chemicalsymbols
+ $\@@dochemicalstyle{\@@localchemicalformat#1}$}}}
+
+\def\executechemicalTEXT[#1]%
+ {\doifnot\@@chemicallocation\v!intext
+ {\setbox\chemicalsymbols\hbox
+ {\box\chemicalsymbols#1}}}
+
+%\def\executechemicalLOW[#1]%
+% {\setlowsubscripts}
+%
+%\def\executechemicalHIGH[#1]%
+% {\sethighsubscripts}
+
+\def\putchemicalrule#1#2#3#4%
+ {\ifcase\chemicaldrawingmode
+ \putrule from {#1} {#2} to {#3} {#4}
+ \or
+ \psline(#1,#2)(#3,#4)%
+ \or
+ \bgroup
+ \!!counta=#1\!!countb=#2\!!countc=#3\!!countd=#4\relax
+ \global\MPdrawingdonetrue
+ \setchemicalattributes
+ \startMPdrawing
+ x1 := \MPdivten[\the\!!counta]u ;
+ y1 := \MPdivten[\the\!!countb]u ;
+ x2 := \MPdivten[\the\!!countc]u ;
+ y2 := \MPdivten[\the\!!countd]u ;
+ draw z1--z2 ;
+ \stopMPdrawing
+ \egroup
+ \fi}
+
+\def\executechemicalcomplex#1%
+ {\bgroup
+ \putchemicalrule {0} {-\@@chemicalbottom} {0} {\@@chemicaltop}%
+ \putchemicalrule {0} {\@@chemicaltop} {#1150} {\@@chemicaltop}%
+ \putchemicalrule {0} {-\@@chemicalbottom} {#1150} {-\@@chemicalbottom}%
+ \egroup}
+
+\def\executechemicalOPENCOMPLEX[#1]%
+ {\executechemicalcomplex+\ignorespaces
+ \executechemicalSPACE[]}
+
+\def\executechemicalCLOSECOMPLEX[#1]%
+ {\executechemicalSPACE[]%
+ \executechemicalcomplex-\ignorespaces}
+
+% nog niet door midden as!
+
+\def\executechemicalverticalsymbol#1#2%
+ {\executechemicalTEXT
+ [$\left#1\relax
+ \dimen0=\@@chemicalunit
+ \scratchcounter=\@@chemicaltop
+ \advance\scratchcounter by \@@chemicalbottom
+ \dimen0=\scratchcounter\dimen0
+ \vcenter to \dimen0{}
+ \dimen2=\@@chemicalunit
+ \dimen2=\@@chemicalright\dimen0
+ \vcenter{\leftskip1em\hsize\dimen2\relax\strut#2\strut}%
+ \right.$]}%
+
+\def\executechemicalUPARROW[#1]%
+ {\executechemicalverticalsymbol\uparrow{#1}}
+
+\def\executechemicalDOWNARROW[#1]%
+ {\executechemicalverticalsymbol\downarrow{#1}}
+
+\def\executechemicalUPDOWNARROW[#1]%
+ {\executechemicalverticalsymbol\updownarrow{#1}}
+
+\let\setchemicalattributes\relax
+
+\setupchemical
+ [\c!width=0,
+ \c!height=0,
+ \c!left=0,
+ \c!right=0,
+ \c!top=0,
+ \c!bottom=0,
+ \c!bodyfont=\the\bodyfontsize,
+ \c!resolution=\outputresolution,
+ \c!scale=\v!medium,
+ \c!size=\v!medium,
+ \c!textsize=\v!big,
+ \c!frame=\v!off,
+ \c!axis=\v!off,
+ \c!state=\v!start,
+ \c!style=\rm,
+ \c!location=,
+ \c!option=,
+ \c!offset=LOW,
+ \c!alternative=1,
+ \c!color=,
+ \c!rulethickness=,
+ \c!rulecolor=,
+ \c!factor=1]
+
+% Tijdelijk plaatsen we deze extra macro's hier.
+%
+% mathontop: \mtop {} {}
+% textontop: \ttop {} {}
+
+\def\putontop#1#2%
+ {\vbox
+ {\halign
+ {\strut\hss##\hss\cr
+ #1\cr
+ #2\cr}}}
+
+\def\ttop#1#2%
+ {\putontop{\tx#1}{#2}}
+
+\def\mtop#1#2%
+ {\vbox
+ {\offinterlineskip
+ \halign
+ {\hss##\hss\cr
+ $\scriptscriptstyle#1$\cr
+ \noalign{\vskip.5ex}%
+ $#2$\cr}}}
+
+\def\ctop#1#2%
+ {\vbox
+ {\offinterlineskip
+ \halign
+ {\hss##\hss\cr
+ $\@@dochemicalstyle{\@@localchemicalformat\scriptscriptstyle#1}$\cr
+ \noalign{\vskip.5ex}%
+ $\@@dochemicalstyle{\@@localchemicalformat#2}$\cr}}}
+
+%D Here are a couple of goodies:
+%D
+%D \startitemize
+%D \item styles hooked into \CONTEXT\ style mechanism
+%D \item support for color and rulethickness (mp mode only)
+%D \item position tracking
+%D \stopitemize
+
+\let\@@chemicalrulecolor\empty
+\let\@@chemicalcolor \empty
+
+\def\setchemicalattributes
+ {\scratchdimen\@@chemicalrulethickness
+ \def\chemicalattributes
+ {withpen pencircle scaled \the\scratchdimen\space
+ withcolor }%
+ \doifelsenothing\@@chemicalrulecolor
+ {\edef\chemicalattributes{\chemicalattributes black}}
+ {\edef\chemicalattributes{\chemicalattributes \MPcolor{\@@chemicalrulecolor}}}%
+ \startMPdrawing
+ drawoptions (\chemicalattributes) ;
+ \stopMPdrawing}
+
+\unexpanded\def\@@dochemicalcolor
+ {\dousecolorparameter\@@chemicalcolor}
+
+\unexpanded\def\@@dochemicalstyle
+ {\dousestyleparameter\@@chemicalstyle}
+
+\setupchemical
+ [\c!rulethickness=\linewidth,
+ \c!rulecolor=,
+ \c!color=]
+
+\def\cpos#1#2%
+ {\iftrialtypesetting
+ #2%
+ \else
+ \bgroup
+ \globalpushmacro\dowithchemical
+ \gdef\dowithchemical##1{\hpos{#1}{##1}\globalpopmacro\dowithchemical}%
+ #2%
+ \egroup
+ \fi}
+
+\protect \endinput
+
+% \startchemical[axis=on,frame=yes]
+% \chemical[SIX,ROT2,B,R36,RZ6][CH_3]
+% \chemical[PB:RZ3,ONE,Z05,SB5,EP37,PE][O,H]
+% \stopchemical
+
+% \startchemical[size=big,scale=small,axis=on,frame=yes,factor=1.5]
+% \chemical[SIX,ROT2,B,R36,RZ6][CH_3]
+% \chemical[PB:RZ3,ONE,Z05,SB5,EP37,PE][O,H]
+% \stopchemical
+
+% \startchemical[size=big,scale=medium,axis=on,frame=yes,factor=1.5]
+% \chemical[SIX,ROT2,B,R36,RZ6][CH_3]
+% \chemical[PB:RZ3,ONE,Z05,SB5,EP37,PE][O,H]
+% \stopchemical
+
+% \startchemical[size=big,scale=big,axis=on,frame=yes,factor=1.5]
+% \chemical[SIX,ROT2,B,R36,RZ6][CH_3]
+% \chemical[PB:RZ3,ONE,Z05,SB5,EP37,PE][O,H]
+% \stopchemical
diff --git a/tex/context/modules/mkiv/s-art-01.mkiv b/tex/context/modules/mkiv/s-art-01.mkiv
new file mode 100644
index 000000000..601ee1adc
--- /dev/null
+++ b/tex/context/modules/mkiv/s-art-01.mkiv
@@ -0,0 +1,62 @@
+\unprotect
+
+\startmodule[art-01]
+
+\definemeasure [article:margin] [\paperheight/20] % was 15, see xtables-mkiv for results
+\definemeasure [overview:margin] [\paperheight/30]
+
+\definelayout
+ [article]
+ [\c!topspace=\measure{article:margin},
+ \c!bottomspace=\measure{article:margin},
+ \c!backspace=\measure{article:margin},
+ \c!header=\measure{article:margin},
+ \c!footer=0pt,
+ \c!width=\v!middle,
+ \c!height=\v!middle]
+
+
+\definelayout
+ [overview]
+ [\c!topspace=\measure{overview:margin},
+ \c!bottomspace=\measure{overview:margin},
+ \c!backspace=\measure{overview:margin},
+ \c!header=\measure{overview:margin},
+ \c!footer=0pt,
+ \c!width=\v!middle,
+ \c!height=\v!middle]
+
+\setuplayout
+ [article]
+
+\setupbodyfont
+ [dejavu,10pt] % 12pt is just to large and we use this for all kind of demos
+
+\setupwhitespace
+ [\v!big]
+
+\setuphead
+ [\v!chapter]
+ [\c!style=\bfc,
+ \c!headerstate=\v!high,
+ \c!interaction=\v!all]
+
+\setuphead
+ [\v!section]
+ [\c!style=\bfb]
+
+\setuphead
+ [\v!subsection]
+ [\c!style=\bfa]
+
+\setuphead
+ [\v!subsubsection]
+ [\c!style=\bf,
+ \c!after=]
+
+\setuplist
+ [\c!interaction=\v!all]
+
+\protect
+
+\stopmodule
diff --git a/tex/context/modules/mkiv/s-def-01.mkiv b/tex/context/modules/mkiv/s-def-01.mkiv
new file mode 100644
index 000000000..49e585bd0
--- /dev/null
+++ b/tex/context/modules/mkiv/s-def-01.mkiv
@@ -0,0 +1,10 @@
+% yes or no
+
+\unprotect
+
+\startsetups defaults:frontpart:pagenumbers:roman
+ \defineconversionset[\c!frontpart:\c!pagenumber][][romannumerals]
+ \setupuserpagenumber[\c!way=\v!by\v!block]
+\stopsetups
+
+\protect
diff --git a/tex/context/modules/mkiv/s-figures-names.mkiv b/tex/context/modules/mkiv/s-figures-names.mkiv
new file mode 100644
index 000000000..a2782efc9
--- /dev/null
+++ b/tex/context/modules/mkiv/s-figures-names.mkiv
@@ -0,0 +1,99 @@
+%D \module
+%D [ file=s-figures-names.mkiv,
+%D version=2017.07.17,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Show Figure Names,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Normally this module will be run someplace at the end of a document in some kind of tracing
+%D mode. We could hook it into \type {\stoptext} but better leave it up to the user. Loading
+%D this module upfront will not show anything useful. The main reason for making this module
+%D was that we wanted to report weird figurenames: spaces, multiple hyphens in a row, mixed
+%D hyphens and underscores, inconsistently mixed upper and lowercase, etc.
+%D
+%D If you only want info in the logfile, you can use:
+%D
+%D \starttyping
+%D \enabletrackers[graphics.lognames]
+%D \stoptyping
+%D
+%D This directive is persistent and can be issued any time before the end of the run.
+
+\startmodule[figures-names]
+
+\startluacode
+
+local context = context
+local ctx_NC = context.NC
+local ctx_NR = context.NR
+local ctx_red = context.red
+local ctx_starttabulate = context.starttabulate
+local ctx_stoptabulate = context.stoptabulate
+local ctx_hyphenatedname = context.hyphenatedfilename
+
+trackers.enable("graphics.lognames")
+
+context.start()
+
+ context.switchtobodyfont { "tt,small" }
+
+ local template = { "|Bl|p|" }
+
+ for _, data in table.sortedhash(figures.found) do
+ ctx_starttabulate(template)
+ local badname = data.badname
+ local found = data.found
+ ctx_NC()
+ context("asked name")
+ ctx_NC()
+ if badname then
+ ctx_red()
+ end
+ context(data.askedname)
+ ctx_NC() ctx_NR()
+ if found then
+ ctx_NC()
+ context("format")
+ ctx_NC()
+ context(data.format)
+ ctx_NC() ctx_NR()
+ ctx_NC()
+ context("found file")
+ ctx_NC()
+ ctx_hyphenatedname(data.foundname)
+ -- context(data.foundname)
+ ctx_NC() ctx_NR()
+ ctx_NC()
+ context("used file")
+ ctx_NC()
+ ctx_hyphenatedname(data.fullname)
+ -- context(data.fullname)
+ ctx_NC() ctx_NR()
+ if badname then
+ ctx_NC()
+ context("comment")
+ ctx_NC()
+ context("bad name")
+ ctx_NC() ctx_NR()
+ end
+ else
+ ctx_NC()
+ context("comment")
+ ctx_NC()
+ context(data.comment or "not found")
+ ctx_NC() ctx_NR()
+ end
+ ctx_stoptabulate()
+ end
+
+context.stop()
+
+\stopluacode
+
+\stopmodule
diff --git a/tex/context/modules/mkiv/s-fnt-10.mkiv b/tex/context/modules/mkiv/s-fnt-10.mkiv
new file mode 100644
index 000000000..731c4be39
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fnt-10.mkiv
@@ -0,0 +1,169 @@
+%D \module
+%D [ file=s-fnt-01,
+%D version=2006.10.10, % guess
+%D title=\CONTEXT\ Style File,
+%D subtitle=Listing Glyphs in Large Fonts,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% sort of obsolete
+
+\startluacode
+local fontdata = fonts.hashes.identifiers
+
+-- function fonts.tracers.show_all()
+-- local tfmdata = fontdata[font.current()]
+-- if tfmdata and tfmdata.shared then
+-- local NC, NR, char = context.NC, context.NR, context.char
+-- context.starttabulate { "|l|r|c|" }
+-- for unicode, description in fonts.iterators.characters(tfmdata) do
+-- NC() context(description.name) NC() context(unicode) NC() char(unicode) NC() NR()
+-- end
+-- context.stoptabulate()
+-- end
+-- end
+
+local context = context
+local escaped = context.escaped
+
+function fonts.tracers.show_all()
+ local tfmdata = fontdata[font.current()]
+ if tfmdata then
+ local NC, NR, HL, char, bold, tttf = context.NC, context.NR, context.HL, context.char, context.bold, context.tttf
+ local descriptions = tfmdata.descriptions or { }
+ local data = characters.data
+ -- context.setuptabulate { header = "repeat" }
+ context.starttabulatehead()
+ NC() bold("unicode")
+ NC() bold("visual")
+ NC() bold("index")
+ NC() bold("glyph")
+ NC() bold("adobe")
+ NC() bold("context")
+ NC() NR()
+ context.stoptabulatehead()
+ context.starttabulate { "|l|c|l|p|p|p|" }
+ for unicode, chr in fonts.iterators.characters(tfmdata) do
+ local des, dat = descriptions[unicode], data[unicode]
+ local index = chr.index or 0
+ local cname = (dat and dat.contextname) or ""
+ local aname = (dat and dat.adobename) or ""
+ local gname = (des and des.name) or ""
+ local mname = dat and dat.mathname
+ if type(mname) ~= "string" then
+ mname = ""
+ end
+ local mspec = dat and dat.mathspec
+ if mspec then
+ for m=1,#mspec do
+ local n = mspec[m].name
+ if n then
+ if mname == "" then
+ mname = n
+ else
+ mname = mname .. " " .. n
+ end
+ end
+ end
+ end
+ if mname ~= "" then
+ mname = "m: " .. mname
+ if cname ~= "" then
+ cname = cname .. " " .. mname
+ else
+ cname = mname
+ end
+ end
+ NC() tttf() context("U+%05X",unicode)
+ NC() char(unicode)
+ NC() tttf() context("%05X",index)
+ NC() if gname ~= "" then tttf() escaped(gname) end
+ NC() if aname ~= "" then tttf() context(aname) end
+ NC() if cname ~= "" then tttf() context(cname) end
+ NC() NR()
+ end
+ context.stoptabulate()
+ else
+ context("problems")
+ end
+end
+
+function fonts.tracers.show_glyphs()
+ local tfmdata = fontdata[font.current()]
+ if tfmdata then
+ for unicode, chr in fonts.iterators.characters(tfmdata) do
+ context.showglyph(unicode)
+ end
+ end
+end
+\stopluacode
+
+\unexpanded\def\ShowCompleteFont#1#2#3%
+ {\bgroup
+ \page
+ \font\TestFont=#1 at #2
+ \setuplayout[style=\TestFont]
+ \setupheadertexts[]
+ \setupfootertexts[#1 -- \pagenumber]
+ \setupfootertexts[pagenumber]
+ \setuplayout[width=middle,height=middle,topspace=1cm,backspace=1cm]
+ \TestFont
+ \nonknuthmode
+ \startcolumns[n=#3]
+ \TestFont
+ \ctxlua { fonts.tracers.show_all() }
+ \stopcolumns
+ \page
+ \egroup}
+
+\unexpanded\def\ShowAllGlyphs#1#2#3%
+ {\bgroup
+ \page
+ \def\showglyph##1{\dontleavehmode\strut\char##1\relax\par}
+ \font\TestFontA=#1 at 12pt
+ \font\TestFontB=#1 at #2
+ \setuplayout[style=\TestFontA]
+ \setupheadertexts[]
+ \setupfootertexts[#1\space\endash\space\pagenumber]
+ \setuplayout[width=middle,height=middle,topspace=1cm,backspace=1cm,header=1cm,footer=2cm]
+ \TestFontB \setupinterlinespace[line=1.2\dimexpr#2\relax] \raggedcenter
+ \nonknuthmode
+ \startcolumns[n=#3]
+ \TestFontB
+ \ctxlua { fonts.tracers.show_glyphs() }
+ \stopcolumns
+ \page
+ \egroup}
+
+\continueifinputfile{s-fnt-10.mkiv}
+
+\starttext
+
+% \ShowCompleteFont{name:dejavusansmono}{10pt}{1}
+% \ShowCompleteFont{name:dejavuserif}{10pt}{2}
+% \ShowCompleteFont{name:officinasansbookitcregular}{10pt}{2}
+% \ShowCompleteFont{name:officinaserifbookitcregular}{10pt}{2}
+% \ShowCompleteFont{name:serpentineserifeflight}{10pt}{2}
+\ShowCompleteFont{name:lmroman10-regular}{10pt}{1}
+% \ShowCompleteFont{name:lmtypewriter10-regular}{10pt}{2}
+% \ShowCompleteFont{lt55485}{10pt}{2}
+% \ShowCompleteFont{lmr10}{10pt}{2}
+% \ShowCompleteFont{lbr}{10pt}{2}
+% \ShowCompleteFont{name:Cambria}{10pt}{2}
+% \ShowCompleteFont{name:CambriaMath}{10pt}{2}
+% \ShowCompleteFont{name:texgyrepagella-regular}{10pt}{2}
+% \ShowCompleteFont{name:texgyrechorus-mediumitalic}{10pt}{2}
+% \ShowAllGlyphs {name:texgyrepagella-regular} {48pt}{2}
+% \ShowAllGlyphs {name:texgyrechorus-mediumitalic}{48pt}{2}
+% \ShowCompleteFont{name:euler10-regular}{10pt}{2}
+
+% \ShowCompleteFont{name:palatinosansinformalcombold}{20pt}{2}
+% \ShowCompleteFont{name:palatinonovaregular}{11pt}{2}
+% \ShowCompleteFont{pirat.ttf}{12pt}{1}
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-fnt-20.mkiv b/tex/context/modules/mkiv/s-fnt-20.mkiv
new file mode 100644
index 000000000..584043099
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fnt-20.mkiv
@@ -0,0 +1,161 @@
+%D \module
+%D [ file=s-fnt-20,
+%D version=2009.01.10,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Tracing Feature Application (1),
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This file is used by Idris and me in the process of getting the
+%D best arabic fonts getting rendered best. As such it might change.
+
+% \setvariables
+% [otftracker]
+% [title=Reverse Chaining,
+% figure=test-003-volt.pdf,
+% font=husayni,
+% sample={ببب بببب ببببب بببببب}]
+
+\def\checkedotftrackerfeature #1{otftracker-\ifnum\featureattribute{otftracker-#1}=\zerocount default\else#1\fi}
+\def\checkedotftrackerdirection#1{\csname otftracker-direction-\ifcsname otftracker-direction-#1\endcsname#1\else default\fi\endcsname}
+
+% we can consider adding a dir key to features
+
+\definefontfeature
+ [otftracker-default]
+ [default]
+ [mode=node]
+
+\definefontfeature
+ [otftracker-arabtype]
+ [mode=node,analyze=yes,
+ language=dflt,script=arab,ccmp=yes,
+ init=yes,medi=yes,fina=yes,isol=yes,
+ liga=yes,dlig=yes,rlig=yes,clig=yes,
+ mark=yes,mkmk=yes,kern=yes,curs=yes]
+
+% \definefontfeature
+% [otftracker-husayni]
+% [analyze=yes,mode=node,
+% language=dflt,script=arab,ccmp=yes,
+% init=yes,medi=yes,fina=yes,isol=yes,
+% calt=yes,
+% mark=yes,mkmk=yes,kern=yes,curs=yes]
+
+\definefontfeature
+ [otftracker-husayni]
+ [analyze=yes,mode=node,
+ language=dflt,script=arab,
+ init=yes,medi=yes,fina=yes,
+ rlig=yes,
+ ccmp=yes,
+ ss01=yes, % full Allah, Muhammad, Allahumma
+ ss05=yes, % full Jiim stacking
+ ss09=yes, % full Haa stacking
+ ss10=yes, % partial dipped Miim
+ ss13=yes, % full stacked Miim
+ ss15=yes, % full stacked Laam-on-Miim
+ ss17=yes, % full stacked Ayn-on-Miim
+ ss19=yes, % LM_im
+ ss24=yes, % BX specials
+ ss25=yes, % LH_im specials
+ ss26=yes, % full Yaa.final specials
+ ss27=yes, % partial thin Miim.final
+ ss31=yes, % partial Raa.final contexts
+ ss34=yes, % partial Raa.final contexts
+ ss35=yes, % full Kaaf contexts
+ ss36=yes, % full Laam contexts
+ ss37=yes, % Miim-Miim contexts
+ ss38=yes, % fancy thin Haa.medial-Miim.final
+ ss39=yes, % high and low Baa strings
+ ss40=yes, % diagonal entry
+ ss41=yes, % initial alternates
+ mark=yes,mkmk=yes,kern=yes,curs=yes]
+
+\definefontfeature
+ [otftracker-simplenaskhi]
+ [analyze=yes,mode=node,
+ language=dflt,script=arab,
+ init=yes,medi=yes,fina=yes,calt=yes,
+ rlig=yes,liga=yes,dlig=yes,
+ mark=yes,mkmk=yes,kern=yes,curs=yes]
+
+\setvalue{otftracker-direction-arabtype}{r2l}
+\setvalue{otftracker-direction-husayni}{r2l}
+\setvalue{otftracker-direction-simplenaskhi}{r2l}
+\setvalue{otftracker-direction-default}{}
+
+\setvariables
+ [otftracker]
+ [font=Serif,
+ features=\checkedotftrackerfeature{\getvariable{otftracker}{font}},
+ direction=\checkedotftrackerdirection{\getvariable{otftracker}{font}},
+ size=48pt,
+ figure=,
+ title=Feature Check,
+ sample=no sample,
+ set=\setups{otftracker}]
+
+\setuplayout
+ [topspace=1.5cm,
+ backspace=1.5cm,
+ width=middle,
+ height=middle,
+ header=1.5cm,
+ footer=1.5cm]
+
+\setuphead
+ [chapter]
+ [header=high,
+ number=no]
+
+\setupfootertexts
+ [chapter]
+
+\setupalign
+ [flushleft]
+
+\setupcolors
+ [state=start]
+
+\startsetups otftracker
+ \setupbodyfont[tt,10pt]
+ \starttext
+ \normalexpanded{\startchapter[title={\getvariable{otftracker}{title}}]}
+ \doifsomething {\getvariable{otftracker}{figure}} {
+ \startlinecorrection
+ \externalfigure[\getvariable{otftracker}{figure}][maxwidth=\hsize,frame=on]
+ \stoplinecorrection
+ }
+ \showotfcomposition
+ {\getvariable{otftracker}{font}*\getvariable{otftracker}{features} at \getvariable{otftracker}{size}}
+ {\getvariable{otftracker}{direction}}
+ {\getvariable{otftracker}{sample}}
+ \stopchapter
+ \stoptext
+\stopsetups
+
+\continueifinputfile{s-fnt-20.mkiv}
+
+\usemodule[art-01]
+
+\setupbodyfont
+% [cambria]
+ [dejavu]
+
+\starttext
+
+\setvariables
+ [otftracker]
+ [font=Serif,
+ size=48pt,
+ figure=,
+ title=Feature Check,
+ sample={affiliation}]
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-fnt-21.mkiv b/tex/context/modules/mkiv/s-fnt-21.mkiv
new file mode 100644
index 000000000..10d5a4623
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fnt-21.mkiv
@@ -0,0 +1,64 @@
+%D \module
+%D [ file=s-fnt-20,
+%D version=2009.01.10,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Tracing Feature Application (2),
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This module is related to:
+
+\usemodule[fnt-20]
+
+\setvariables
+ [otftracker]
+ [set=\setups{araball}]
+
+\startsetups araball
+ \begingroup
+ \setupcolors[state=start]%
+ \enabletrackers[otf.analyzing]% beware, kind of global
+ \ruledhbox \bgroup
+ \definedfont[\getvariable{otftracker}{font}*\getvariable{otftracker}{features} at \getvariable{otftracker}{size}]%
+ \ifnum\getvariable{otftracker}{direction}<0 \textdir TRT\else\ifnum\getvariable{otftracker}{direction}>0 \textdir TLT\fi\fi\relax
+ \getvariable{otftracker}{sample}%
+ \egroup
+ \disabletrackers[otf.analyzing]%
+ \endgroup
+\stopsetups
+
+\unexpanded\def\ShowOtfTrackerSample#1%
+ {\doiffile{#1}
+ {\blank
+ \startlinecorrection
+ \vbox \bgroup
+ \forgetall
+ \setbox\scratchbox\hbox{\component #1 \relax}
+ \hbox{\copy\scratchbox\quad\lower\dp\scratchbox\hbox{\ruledhbox{\externalfigure[#1-volt.pdf][height=\htdp\scratchbox]}}}
+ \hbox{\strut\tttf#1}%
+ \egroup
+ \stoplinecorrection
+ \blank}}
+
+\endinput
+
+% \usemodule[fnt-21]
+%
+% \starttext
+%
+% \setvariables
+% [otftracker]
+% [direction=-1,
+% sample=لا,
+% title=Test,
+% font=file:arabtype,
+% % font=file:husayni,
+% % font=file:scheherazaderegot,
+% features=arabic]
+%
+% \stoptext
diff --git a/tex/context/modules/mkiv/s-fnt-24.mkiv b/tex/context/modules/mkiv/s-fnt-24.mkiv
new file mode 100644
index 000000000..2e6b9a591
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fnt-24.mkiv
@@ -0,0 +1,83 @@
+%D \module
+%D [ file=s-fnt-24,
+%D version=2009.02.06,
+%D title=\CONTEXT\ Style File,
+%D subtitle=CJK Glyph Combination Testing,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+\def\ShowChineseCombiChar#1#2#3#4%
+ {\blank[small]
+ \dontleavehmode
+ \hbox\bgroup
+ \dontcomplain
+ \setstrut
+ \hbox to 7em{\ruledhbox{\char#1}\hskip.25em\type{+}\hskip.25em\ruledhbox{\char#2}\hskip.25em\type{=}\hskip.25em\ruledhbox{\char#1\char#2}\hss}\relax
+ \ruledvtop{\hsize1em\char#1\char#2}\relax
+ \hskip2em
+ \ruledvtop{\hsize.625em\char#1\char#2}\relax
+ \hskip2em
+ \ruledvtop{\hsize1.5em\char#1\char#2}\relax
+ \hskip2em
+ \type{#3 + #4}\relax
+ \egroup
+ \blank[small]}
+
+\startluacode
+local example = {
+ korean = 0x0AC00,
+ chinese = 0x04E55,
+ full_width_open = 0x03008,
+ full_width_close = 0x03009,
+ half_width_open = 0x02018,
+ half_width_close = 0x02019,
+ hyphen = 0x02026,
+ non_starter = 0x03005,
+ other = 0x0004D, -- M
+}
+
+function fonts.analyzers.cjktest(first,second)
+ for k, v in next, example do
+ if (not first or first == "") or first == k then
+ for kk, vv in next, example do
+ if (not second or second == "") or second == kk then
+ context.ShowChineseCombiChar(v,vv,k,kk)
+ end
+ end
+ end
+ end
+end
+\stopluacode
+
+\unexpanded\def\ShowCombinationsKorean
+ {\dodoubleempty\doShowCombinationsKorean}
+
+\def\doShowCombinationsKorean[#1][#2]%
+ {\startpacked
+ \setscript[hangul]
+ \setupcolors[\c!state=\v!start]
+ \enabletrackers[cjk.analyzing]
+ \ctxlua{fonts.analyzers.cjktest("#1","#2")}\par % !
+ \disabletrackers[cjk.analyzing]
+ \stoppacked}
+
+\unexpanded\def\ShowCombinationsChinese
+ {\dodoubleempty\doShowCombinationsChinese}
+
+\def\doShowCombinationsChinese[#1][#2]%
+ {\startpacked
+ \setscript[hanzi]
+ \setupcolors[\c!state=\v!start]
+ \enabletrackers[cjk.analyzing]
+ \ctxlua{fonts.analyzers.cjktest("#1","#2")}\par % !
+ \disabletrackers[cjk.analyzing]
+ \stoppacked}
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/s-fonts-charts.mkiv b/tex/context/modules/mkiv/s-fonts-charts.mkiv
new file mode 100644
index 000000000..e94b52a2e
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-charts.mkiv
@@ -0,0 +1,204 @@
+%D \module
+%D [ file=s-fonts-charts,
+%D version=2015.08.08,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Show Fonts Charts,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% begin info
+%
+% title : show unicode blocks
+%
+% comment : show charts of a given fgont
+%
+% end info
+
+\startmodule[fonts-charts]
+
+% \registerctxluafile{s-fonts-charts}{}
+\starttexdefinition unexpanded FontChartSetSlot #1#2
+ \dontleavehmode
+ \setbox\scratchbox\vbox to 1cm \bgroup
+ \vss
+ \hbox to 1cm \bgroup
+ \infofont \hss\raise.25mm\hbox{#2}\hss
+ \egroup
+ \egroup
+ \wd\scratchbox0mm
+ \ht\scratchbox5mm
+ \dp\scratchbox5mm
+ \box\scratchbox
+ \setbox\scratchbox\ruledhbox to 1cm \bgroup
+ \hss\char#1\hss
+ \egroup
+ \ht\scratchbox5mm
+ \dp\scratchbox5mm
+ \box\scratchbox
+\stoptexdefinition
+
+\starttexdefinition unexpanded FontChartNoSlot
+ \dontleavehmode
+ \setbox\scratchbox\hbox to 1cm \bgroup
+ % empty
+ \egroup
+ \ht\scratchbox5mm
+ \dp\scratchbox5mm
+ \box\scratchbox
+\stoptexdefinition
+
+\starttexdefinition unexpanded FontChartSetLegend #1
+ \dontleavehmode
+ \setbox\scratchbox\hbox to 1cm \bgroup
+ \infofont\hss#1\hss
+ \egroup
+ \ht\scratchbox5mm
+ \dp\scratchbox5mm
+ \box\scratchbox
+\stoptexdefinition
+
+\starttexdefinition unexpanded FontChartSetCaption #1#2
+ \dontleavehmode
+ \setbox\scratchbox\hbox to 16cm \bgroup
+ \infofontbold\hskip1cm#1: #2\hss
+ \egroup
+ \ht\scratchbox7.5mm
+ \dp\scratchbox2.5mm
+ \box\scratchbox
+\stoptexdefinition
+
+\starttexdefinition unexpanded FontChartGap
+ \kern .5mm
+\stoptexdefinition
+
+\startluacode
+ local div, mod = math.div, math.mod
+ local formatters = string.formatters
+
+ moduledata.fonts = moduledata.fonts or { }
+ moduledata.fonts.charts = moduledata.fonts.charts or { }
+
+ function moduledata.fonts.charts.show(settings)
+
+ local settings = utilities.parsers.settings_to_hash(settings)
+
+ local filename = settings.filename or ""
+ local fontid = true
+ local newpage = settings.page == interfaces.variables.yes
+
+ if filename ~= "" then
+ fontid = fonts.definers.internal({ name = filename, size = "10pt" },"chartfont")
+ end
+
+ local ranges = { }
+ local data = fonts.hashes.identifiers[fontid]
+ local private = fonts.constructors.privateoffset
+ local chars = data.characters
+
+ for u in table.sortedhash(chars) do
+ if u >= private then
+ break
+ else
+ ranges[div(u,0xFF)] = true
+ end
+ end
+
+ local ctx_setlegend = context.FontChartSetLegend
+ local ctx_noslot = context.FontChartNoSlot
+ local ctx_setslot = context.FontChartSetSlot
+ local ctx_setcaption = context.FontChartSetCaption
+ local ctx_par = context.par
+ local ctx_gap = context.FontChartGap
+
+ for r in table.sortedhash(ranges) do
+ if newpage then
+ context.page()
+ end
+ context.startframed { offset = "overlay", frame = "off", align = "normal" }
+ if filename ~= "" then
+ context.chartfont()
+ end
+ context.dontcomplain()
+ context.offinterlineskip()
+ ctx_noslot()
+ for i=0,0xF do
+ ctx_setlegend(formatters["%03X"](r*0x10+i))
+ end
+ r = r * 0xFF
+ ctx_par()
+ for i=0,0xF do
+ ctx_setlegend(formatters["%0X"](i))
+ ctx_gap()
+ for j=0,0xF do
+ local u = r + i*0x10 + j
+ local d = chars[u]
+ if d then
+ ctx_setslot(u,formatters["%04X"](u))
+ else
+ ctx_noslot()
+ end
+ if j ~= 0xF then
+ ctx_gap()
+ end
+ end
+ ctx_par()
+ ctx_gap()
+ end
+ ctx_setcaption(formatters["%04X-%04X"](r,r+0xFF),file.basename(data.properties.filename))
+ ctx_par()
+ context.stopframed()
+ if newpage then
+ context.page()
+ end
+ end
+ end
+\stopluacode
+
+\installmodulecommandluasingle \showfontchart {moduledata.fonts.charts.show}
+
+\stopmodule
+
+\continueifinputfile{s-fonts-charts.mkiv}
+
+\usemodule[art-01]
+
+\starttext
+
+ \showfontchart[filename=LucidaBrightOT.otf,page=yes]
+ \showfontchart[filename=LucidaBrightOT-Demi.otf,page=yes]
+ \showfontchart[filename=LucidaBrightOT-DemiItalic.otf,page=yes]
+ \showfontchart[filename=LucidaBrightOT-Italic.otf,page=yes]
+
+ \showfontchart[filename=LucidaSansOT.otf,page=yes]
+ \showfontchart[filename=LucidaSansOT-Demi.otf,page=yes]
+ \showfontchart[filename=LucidaSansOT-DemiItalic.otf,page=yes]
+ \showfontchart[filename=LucidaSansOT-Italic.otf,page=yes]
+
+ \showfontchart[filename=LucidaSansTypewriterOT.otf,page=yes]
+ \showfontchart[filename=LucidaSansTypewriterOT-Bold.otf,page=yes]
+ \showfontchart[filename=LucidaSansTypewriterOT-BoldOblique.otf,page=yes]
+ \showfontchart[filename=LucidaSansTypewriterOT-Oblique.otf,page=yes]
+
+ \showfontchart[filename=LucidaConsoleDK.otf,page=yes]
+ \showfontchart[filename=LucidaConsoleDK-Bold.otf,page=yes]
+ \showfontchart[filename=LucidaConsoleDK-BoldItalic.otf,page=yes]
+ \showfontchart[filename=LucidaConsoleDK-Italic.otf,page=yes]
+
+ \showfontchart[filename=LucidaGrandeMonoDK.otf,page=yes]
+ \showfontchart[filename=LucidaGrandeMonoDK-Bold.otf,page=yes]
+ \showfontchart[filename=LucidaGrandeMonoDK-BoldItalic.otf,page=yes]
+ \showfontchart[filename=LucidaGrandeMonoDK-Italic.otf,page=yes]
+
+ \showfontchart[filename=LucidaBrightMathOT.otf,page=yes]
+ \showfontchart[filename=LucidaBrightMathOT-Demi.otf,page=yes]
+
+ \showfontchart[filename=LucidaBlackletterOT.otf,page=yes]
+ \showfontchart[filename=LucidaCalligraphyOT.otf,page=yes]
+ \showfontchart[filename=LucidaHandwritingOT.otf,page=yes]
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-fonts-coverage.lua b/tex/context/modules/mkiv/s-fonts-coverage.lua
new file mode 100644
index 000000000..dd772d5f0
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-coverage.lua
@@ -0,0 +1,123 @@
+if not modules then modules = { } end modules ['s-fonts-coverage'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-coverage.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.coverage = moduledata.fonts.coverage or { }
+
+local upper, format = string.upper, string.format
+local lpegmatch = lpeg.match
+local concat = table.concat
+
+local context = context
+local NC, NR, HL = context.NC, context.NR, context.HL
+local char, bold, getvalue = context.char, context.bold, context.getvalue
+
+local chardata = characters.data
+
+function moduledata.fonts.coverage.showcomparison(specification)
+
+ specification = interfaces.checkedspecification(specification)
+
+ local fontfiles = utilities.parsers.settings_to_array(specification.list or "")
+ local pattern = upper(specification.pattern or "")
+
+ local present = { }
+ local names = { }
+ local files = { }
+ local chars = { }
+
+ if not pattern then
+ -- skip
+ elseif pattern == "" then
+ pattern = nil
+ elseif tonumber(pattern) then
+ pattern = tonumber(pattern)
+ else
+ pattern = lpeg.oneof(utilities.parsers.settings_to_array(pattern))
+ pattern = (1-pattern)^0 * pattern
+ end
+
+ for i=1,#fontfiles do
+ local fontname = format("testfont-%s",i)
+ local fontfile = fontfiles[i]
+ local fontsize = tex.dimen.bodyfontsize
+ local id, fontdata = fonts.definers.define {
+ name = fontfile,
+ size = fontsize,
+ cs = fontname,
+ }
+ if id and fontdata then
+ for k, v in next, fontdata.characters do
+ present[k] = true
+ end
+ names[#names+1] = fontname
+ files[#files+1] = fontfile
+ chars[#names] = fontdata.characters
+ end
+ end
+
+ local t = { }
+
+ context.starttabulate { "|Tr" .. string.rep("|l",#names) .. "|" }
+ for i=1,#files do
+ local file = files[i]
+ t[#t+1] = i .. "=" .. file
+ NC()
+ context(i)
+ NC()
+ context(file)
+ NC()
+ NR()
+ end
+ context.stoptabulate()
+
+ context.setupfootertexts {
+ table.concat(t," ")
+ }
+
+ context.starttabulate { "|Tl" .. string.rep("|c",#names) .. "|Tl|" }
+ NC()
+ bold("unicode")
+ NC()
+ for i=1,#names do
+ bold(i)
+ NC()
+ end
+ bold("description")
+ NC()
+ NR()
+ HL()
+ for k, v in table.sortedpairs(present) do
+ if k <= 0 then
+ -- ignore
+ elseif k >= 0x110000 then
+ logs.report("warning","weird character %U",k)
+ else
+ local description = chardata[k].description
+ if not pattern or (pattern == k) or (description and lpegmatch(pattern,description)) then
+ NC()
+ context("%05X",k)
+ NC()
+ for i=1,#names do
+ getvalue(names[i])
+ if chars[i][k] then
+ char(k)
+ else
+ -- missing
+ end
+ NC()
+ end
+ context(description)
+ NC()
+ NR()
+ end
+ end
+ end
+ context.stoptabulate()
+
+end
diff --git a/tex/context/modules/mkiv/s-fonts-coverage.mkiv b/tex/context/modules/mkiv/s-fonts-coverage.mkiv
new file mode 100644
index 000000000..c09d943bc
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-coverage.mkiv
@@ -0,0 +1,131 @@
+%D \module
+%D [ file=s-fonts-coverage, % s-fnt-31
+%D version=2011.01.02,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Show Fonts Coverage,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% begin info
+%
+% title : compare glyphs in fonts
+%
+% comment : show glyphs in several fonts alongside in order to to compare coverage
+% status : experimental, used for luatex testing
+%
+% end info
+
+\startmodule[fonts-coverage]
+
+\registerctxluafile{s-fonts-coverage}{}
+
+\installmodulecommandluasingle \showfontcomparison {moduledata.fonts.coverage.showcomparison}
+
+\stopmodule
+
+\continueifinputfile{s-fonts-coverage.mkiv}
+
+\usemodule[art-01]
+
+\starttext
+
+ \showfontcomparison
+ [list={texgyrepagella-regular.otf,texgyretermes-regular.otf,texgyrebonum-regular.otf},
+ pattern=ogonek]
+
+ \page
+
+ % $e=mc²$ ${}²$ $²$ $x²ᶞ$ $x⁽²⁺²⁼²⁺²⁾$ $x²⁺²⁼²⁺²$ $x₅²$ $x²₅²$
+
+ % \startluacode
+ % moduledata.fonts.coverage.showcomparison {
+ % list = {
+ % "texgyrepagella-math.otf",
+ % "texgyretermes-math.otf",
+ % "texgyrebonum-math.otf",
+ % }
+ % }
+ % \stopluacode
+
+ % \startluacode
+ % moduledata.fonts.coverage.showcomparison {
+ % list = "pirat.ttf",
+ % }
+ % \stopluacode
+
+ % \startluacode
+ % moduledata.fonts.coverage.showcomparison {
+ % list = {
+ % "lucidabrightot.otf",
+ % "cambria.ttf",
+ % "iwona-regular.otf",
+ % "texgyrepagella-regular.otf",
+ % "texgyretermes-regular.otf",
+ % "texgyrebonum-regular.otf",
+ % "antpolt-regular.otf",
+ % },
+ % pattern = "OGONEK"
+ % }
+ % \stopluacode
+
+ % \loadfontgoodies[lucida-math]
+ % \loadfontgoodies[lm-math]
+ %
+ % \startluacode
+ % moduledata.fonts.coverage.showcomparison {
+ % list = {
+ % "LucidaBrightRegular.ttf",
+ % "LucidaBrightOne@lucida-math",
+ % "cambria.ttc(Cambria Math)",
+ % "xits-math.otf",
+ % "LMMath10-Regular@lmroman10-math",
+ % },
+ % }
+ % \stopluacode
+
+ % \loadfontgoodies[px-math]
+ % \loadfontgoodies[lm-math]
+ %
+ % \startluacode
+ % moduledata.fonts.coverage.showcomparison {
+ % list = {
+ % "cambria.ttc(Cambria Math)",
+ % "xits-math.otf",
+ % -- "stixmath-regular.otf",
+ % "latinmodern-math.otf",
+ % -- "lucidabrightmathot.otf",
+ % -- "lucidabrightmathot-demi.otf",
+ % "texgyrepagella-math.otf",
+ % "texgyretermes-math.otf",
+ % "texgyrebonum-math.otf",
+ % -- "LMMath10-Regular@lmroman10-math",
+ % -- "pxmath@px-math",
+ % -- "txmath@tx-math",
+ % },
+ % }
+ % \stopluacode
+
+ % \startluacode
+ % moduledata.fonts.coverage.showcomparison {
+ % list = {
+ % "dejavuserif.ttf",
+ % "dejavusans.ttf",
+ % "dejavusansmono.ttf",
+ % "lucidabrightot.otf",
+ % "cambria.ttf",
+ % "iwona-regular.otf",
+ % "texgyrepagella-regular.otf",
+ % "texgyretermes-regular.otf",
+ % "texgyrebonum-regular.otf",
+ % "antpolt-regular.otf",
+ % },
+ % pattern = "CELSIUS,FAHRENHEIT"
+ % }
+ % \stopluacode
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-fonts-features.lua b/tex/context/modules/mkiv/s-fonts-features.lua
new file mode 100644
index 000000000..0a7cf8b13
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-features.lua
@@ -0,0 +1,161 @@
+if not modules then modules = { } end modules ['s-fonts-features'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-features.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.features = moduledata.fonts.features or { }
+
+-- for the moment only otf
+
+local sortedhash = table.sortedhash
+
+local NC, NR, bold = context.NC, context.NR, context.bold
+
+function moduledata.fonts.features.showused(specification)
+
+ specification = interfaces.checkedspecification(specification)
+
+ -- local list = utilities.parsers.settings_to_set(specification.list or "all")
+
+ context.starttabulate { "|T|T|T|T|T|" }
+
+ context.HL()
+
+ NC() bold("feature")
+ NC()
+ NC() bold("description")
+ NC() bold("value")
+ NC() bold("internal")
+ NC() NR()
+
+ context.HL()
+
+ local usedfeatures = fonts.handlers.otf.statistics.usedfeatures
+ local features = fonts.handlers.otf.tables.features
+ local descriptions = fonts.handlers.otf.features.descriptions
+
+ for feature, keys in sortedhash(usedfeatures) do
+ -- if list.all or (list.otf and rawget(features,feature)) or (list.extra and rawget(descriptions,feature)) then
+ local done = false
+ for k, v in sortedhash(keys) do
+ if done then
+ NC()
+ NC()
+ NC()
+ elseif rawget(descriptions,feature) then
+ NC() context(feature)
+ NC() context("+") -- extra
+ NC() context(descriptions[feature])
+ done = true
+ elseif rawget(features,feature) then
+ NC() context(feature)
+ NC() -- otf
+ NC() context(features[feature])
+ done = true
+ else
+ NC() context(feature)
+ NC() context("-") -- unknown
+ NC()
+ done = true
+ end
+ NC() context(k)
+ NC() context(tostring(v))
+ NC() NR()
+ end
+ -- end
+ end
+
+ context.HL()
+
+ context.stoptabulate()
+
+end
+
+local function collectkerns(tfmdata,feature)
+ local combinations = { }
+ local resources = tfmdata.resources
+ local characters = tfmdata.characters
+ local sequences = resources.sequences
+ local lookuphash = resources.lookuphash
+ local feature = feature or "kern"
+ if sequences then
+ for i=1,#sequences do
+ local sequence = sequences[i]
+ if sequence.features and sequence.features[feature] then
+ local lookuplist = sequence.subtables
+ if lookuplist then
+ for l=1,#lookuplist do
+ local lookupname = lookuplist[l]
+ local lookupdata = lookuphash[lookupname]
+ for unicode, data in next, lookupdata do
+ local kerns = combinations[unicode]
+ if not kerns then
+ kerns = { }
+ combinations[unicode] = kerns
+ end
+ for otherunicode, kern in next, data do
+ if not kerns[otherunicode] and kern ~= 0 then
+ kerns[otherunicode] = kern
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ return combinations
+end
+
+local showkernpair = context.showkernpair
+
+function moduledata.fonts.features.showbasekerns(specification)
+ -- assumes that the font is loaded in base mode
+ specification = interfaces.checkedspecification(specification)
+ local id, cs = fonts.definers.internal(specification,"<module:fonts:features:font>")
+ local tfmdata = fonts.hashes.identifiers[id]
+ local done = false
+ for unicode, character in sortedhash(tfmdata.characters) do
+ local kerns = character.kerns
+ if kerns then
+ context.par()
+ for othercode, kern in sortedhash(kerns) do
+ showkernpair(unicode,kern,othercode)
+ end
+ context.par()
+ done = true
+ end
+ end
+ if not done then
+ context("no kern pairs found")
+ context.par()
+ end
+end
+
+function moduledata.fonts.features.showallkerns(specification)
+ specification = interfaces.checkedspecification(specification)
+ local id, cs = fonts.definers.internal(specification,"<module:fonts:features:font>")
+ local tfmdata = fonts.hashes.identifiers[id]
+ local allkerns = collectkerns(tfmdata)
+ local characters = tfmdata.characters
+ if next(allkerns) then
+ for first, pairs in sortedhash(allkerns) do
+ context.par()
+ for second, kern in sortedhash(pairs) do
+ -- local kerns = characters[first].kerns
+ -- if not kerns and pairs[second] then
+ -- -- weird
+ -- end
+ showkernpair(first,kern,second,0)
+ end
+ context.par()
+ end
+ else
+ context("no kern pairs found")
+ context.par()
+ end
+end
diff --git a/tex/context/modules/mkiv/s-fonts-features.mkiv b/tex/context/modules/mkiv/s-fonts-features.mkiv
new file mode 100644
index 000000000..b81b53a71
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-features.mkiv
@@ -0,0 +1,82 @@
+%D \module
+%D [ file=s-fonts-features, % was s-fnt-41, s-fnt-27
+%D version=2012.11.27, % 2010.02.22
+%D title=\CONTEXT\ Style File,
+%D subtitle=Features,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[fonts-features]
+
+\registerctxluafile{s-fonts-features}{}
+
+\installmodulecommandluasingle \showusedfeatures {moduledata.fonts.features.showused}
+\installmodulecommandluasingle \showallkerns {moduledata.fonts.features.showallkerns}
+\installmodulecommandluasingle \showbasekerns {moduledata.fonts.features.showbasekerns}
+
+\def\kernpairheight{\strutheight}
+\def\kernpairdepth {\strutdepth}
+\def\kernpairwidth {\onepoint}
+
+\unexpanded\def\showkernpair#1#2#3% first second kern
+ {\dontleavehmode
+ \hbox \bgroup
+ \scratchdimen#2\scaledpoint
+ \kern\fontcharwd\font#1\relax
+ \ifdim\scratchdimen>\zeropoint
+ \bgroup
+ \darkgreen
+ \vrule width \scratchdimen height \kernpairheight depth \kernpairdepth
+ \egroup
+ \kern-\scratchdimen
+ \else\ifdim\scratchdimen<\zeropoint
+ \kern\scratchdimen
+ \bgroup
+ \darkred
+ \vrule width -\scratchdimen height \kernpairheight depth \kernpairdepth
+ \egroup
+ \else
+ \kern-.5\dimexpr\kernpairwidth\relax
+ \bgroup
+ \darkblue
+ \vrule width \kernpairwidth height \kernpairheight depth \kernpairdepth
+ \egroup
+ \kern-.5\dimexpr\kernpairwidth\relax
+ \fi\fi
+ \kern-\fontcharwd\font#1\relax
+ \char#1\relax
+ \char#3\relax
+ \egroup
+ \space}
+
+\stopmodule
+
+\continueifinputfile{s-fonts-features.mkiv}
+
+\usemodule
+ [art-01]
+
+\starttext
+
+ \showusedfeatures \page
+
+ \definefontfeature[default-base][default][mode=base]
+
+ \start
+ \definedfont[Serif*default-base]
+ \showbasekerns
+ \page
+ \stop
+
+ \start
+ \bf
+ \showallkerns
+ \page
+ \stop
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-fonts-goodies.lua b/tex/context/modules/mkiv/s-fonts-goodies.lua
new file mode 100644
index 000000000..381fc45ea
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-goodies.lua
@@ -0,0 +1,117 @@
+if not modules then modules = { } end modules['s-fonts-goodies'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-goodies.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.goodies = moduledata.fonts.goodies or { }
+
+local NC, NR, HL = context.NC, context.NR, context.HL
+
+local function initialized(specification)
+ specification = interfaces.checkedspecification(specification)
+ local name = specification.name
+ if name then
+ local goodies = fonts.goodies.load(name)
+ if goodies then
+ return specification, goodies
+ end
+ end
+end
+
+function moduledata.fonts.goodies.showstylistics(specification)
+ local specification, goodies = initialized(specification)
+ if goodies then
+ local stylistics = goodies.stylistics
+ if stylistics then
+ context.starttabulate { "|Tl|Tpl|" }
+ HL()
+ NC() context.bold("feature")
+ NC() context.bold("meaning")
+ NC() NR()
+ HL()
+ for feature, meaning in table.sortedpairs(stylistics) do
+ NC() context(feature)
+ NC() context(string.lower(meaning))
+ NC() NR()
+ end
+ HL()
+ context.stoptabulate()
+ end
+ end
+end
+
+function moduledata.fonts.goodies.showfeaturesets(specification)
+ local specification, goodies = initialized(specification)
+ if goodies then
+ local featuresets = goodies.featuresets
+ if featuresets then
+ context.starttabulate { "|Tl|Tpl|" }
+ HL()
+ NC() context.bold("featureset")
+ NC() context.bold("definitions")
+ NC() NR()
+ HL()
+ for featureset, definitions in table.sortedpairs(featuresets) do
+ NC() context.type(featureset) NC()
+ for k, v in table.sortedpairs(definitions) do
+ context("%s=%S",k,v)
+ context.quad()
+ end
+ NC() NR()
+ end
+ HL()
+ context.stoptabulate()
+ end
+ end
+end
+
+function moduledata.fonts.goodies.showcolorschemes(specification)
+ local specification, goodies = initialized(specification)
+ if goodies then
+ local colorschemes = goodies.colorschemes
+ if colorschemes then
+ context.starttabulate { "|Tl|Tpl|" }
+ HL()
+ NC() context.bold("colorscheme")
+ NC() context.bold("numbers")
+ NC() NR()
+ HL()
+ for colorscheme, numbers in table.sortedpairs(colorschemes) do
+ NC() context.type(colorscheme) NC()
+ for i=1,#numbers do
+ context(i)
+ context.quad()
+ end
+ NC() NR()
+ end
+ HL()
+ context.stoptabulate()
+ end
+ end
+end
+
+function moduledata.fonts.goodies.showfiles(specification)
+ local specification, goodies = initialized(specification)
+ if goodies then
+ local files = goodies.files
+ if files and files.list then
+ for filename, specification in table.sortedpairs(files.list) do
+ context.start()
+ context.dontleavehmode()
+ context.definedfont{ filename .. "*default" }
+ context("%s-%s-%s-%s-%s",
+ specification.name or files.name,
+ specification.weight or "normal",
+ specification.style or "normal",
+ specification.width or "normal",
+ specification.variant or "normal")
+ context.par()
+ context.stop()
+ end
+ end
+ end
+end
diff --git a/tex/context/modules/mkiv/s-fonts-goodies.mkiv b/tex/context/modules/mkiv/s-fonts-goodies.mkiv
new file mode 100644
index 000000000..e596507af
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-goodies.mkiv
@@ -0,0 +1,37 @@
+%D \module
+%D [ file=s-fonts-goodies, % s-fnt-26, s-fnt-28
+%D version=2009.10.26,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Goodies Tables,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D More tables will follow here as we have many more goodies by now.
+
+\startmodule[fonts-goodies]
+
+\registerctxluafile{s-fonts-goodies}{}
+
+\installmodulecommandluasingle \showfontgoodiesstylistics {moduledata.fonts.goodies.showstylistics}
+\installmodulecommandluasingle \showfontgoodiesfeaturesets {moduledata.fonts.goodies.showfeaturesets}
+\installmodulecommandluasingle \showfontgoodiescolorschemes {moduledata.fonts.goodies.showcolorschemes}
+\installmodulecommandluasingle \showfontgoodiesfiles {moduledata.fonts.goodies.showfiles}
+
+\stopmodule
+
+\continueifinputfile{s-fonts-goodies.mkiv}
+
+\starttext
+
+ \showfontgoodiesstylistics [name=husayni] \page
+ \showfontgoodiesfeaturesets [name=husayni] \page
+ \showfontgoodiescolorschemes[name=husayni] \page
+
+ \showfontgoodiesfiles [name=antykwapoltawskiego] \page
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-fonts-ligatures.mkiv b/tex/context/modules/mkiv/s-fonts-ligatures.mkiv
new file mode 100644
index 000000000..fc49ec5c5
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-ligatures.mkiv
@@ -0,0 +1,209 @@
+%D \module
+%D [ file=s-fonts-ligatures,
+%D version=2014.12.14,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Show Fonts Ligatures,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% begin info
+%
+% title : show some ligature building in fonts
+%
+% comment : we trace some ligatures that have rather different implementations in fontss
+% status : experimental, used for luatex testing
+%
+% end info
+
+\definefontfeature
+ [otfligaturetest]
+ [analyze=off,
+ ccmp=yes, % brill uses that .. not really ligatures !
+ %clig=yes,
+ script=latn,
+ language=dflt]
+
+\hyphenation{xf-fi-a}
+\hyphenation{xff-i-b}
+\hyphenation{xffi-c}
+\hyphenation{xffid}
+
+\registerhyphenationexception[xf-fi-a]
+\registerhyphenationexception[xff-i-b]
+\registerhyphenationexception[xffi-c]
+\registerhyphenationexception[xffid]
+
+\starttexdefinition showotfligaturescaption #1
+ \bTD [align={flushleft,lohi},nx=3]
+ \nohyphens
+ \ttbf
+ #1
+ \eTD
+\stoptexdefinition
+
+\starttexdefinition showotfligatureslegend #1
+ \bTD [width=6em,align={flushleft,lohi}]
+ \nohyphens \ttxx original
+ \eTD
+ \bTD [width=6em,align={flushleft,lohi}]
+ \nohyphens \ttxx expanded
+ \eTD
+ \bTD [width=6em,align={flushleft,lohi}]
+ \nohyphens \ttxx traditional
+ \eTD
+\stoptexdefinition
+
+\starttexdefinition showotfligaturesline #1#2
+ \bTD[toffset=.5ex,frame=off]
+ \starthyphenation[#1]
+ \LigatureFont
+ \showfontkerns
+ \showdiscretionaries
+ \begstrut#2\endstrut
+ \par
+ \stophyphenation
+ \eTD
+\stoptexdefinition
+
+\def\showotfligaturescells{3}
+%def\showotfligaturesnx {12}
+%def\showotfligatureswidth{18em}
+\def\showotfligaturesnx {\the\numexpr 4*\showotfligaturescells}
+\def\showotfligatureswidth{\the\dimexpr6em*\showotfligaturescells}
+
+\starttexdefinition showotfligaturesbanner #1
+ \bTR[frame=off]
+ \bTD [nx=\showotfligaturesnx,xwidth=\showotfligatureswidth,align={middle,lohi},height=4ex]
+ \tttf #1
+ \eTD
+ \eTR
+\stoptexdefinition
+
+\starttexdefinition showotfligaturescaptions #1
+ \bTR[height=3ex,align={middle,lohi},bottomframe=off]
+ \processcommalist[#1]\showotfligaturescaption
+ \eTR
+ \bTR[height=3ex,align={middle,lohi},topframe=off]
+ \processcommalist[#1]\showotfligatureslegend
+ \eTR
+\stoptexdefinition
+
+\starttexdefinition showotfligatureslineset #1
+ \showotfligaturesline{original} {#1}
+ \showotfligaturesline{expanded} {#1}
+ \showotfligaturesline{traditional}{#1}
+\stoptexdefinition
+
+
+\starttexdefinition showotfligaturesparagraphset #1
+ \showotfligatureslineset {
+ \hsize \zeropoint
+ \lefthyphenmin \plustwo
+ \righthyphenmin\plustwo
+ #1
+ }
+\stoptexdefinition
+
+\starttexdefinition showotfligaturesextremeset #1
+ \showotfligatureslineset {
+ \hsize \zeropoint
+ \lefthyphenmin \plusone
+ \righthyphenmin\plusone
+ #1
+ }
+\stoptexdefinition
+
+\starttexdefinition showotfligatureslines #1
+ \bTR[height=4ex,bottomframe=off]
+ \processcommalist[#1]\showotfligatureslineset
+ \eTR
+\stoptexdefinition
+
+\starttexdefinition showotfligaturesparagraphs #1
+ \bTR[topframe=off]
+ \processcommalist[#1]\showotfligaturesparagraphset
+ \eTR
+\stoptexdefinition
+
+\starttexdefinition showotfligaturesextremes #1
+ \bTR[topframe=off]
+ \processcommalist[#1]\showotfligaturesextremeset
+ \eTR
+\stoptexdefinition
+
+\starttexdefinition showotfligaturesdefaults
+ \bTR
+ \bTD [nx=\showotfligaturesnx,xwidth=\showotfligatureswidth,align=middle,height=4ex,frame=off]
+ \start \LigatureFont fb \stop \quad
+ \start \LigatureFont ff \stop \quad
+ \start \LigatureFont fi \stop \quad
+ \start \LigatureFont fk \stop \quad
+ \start \LigatureFont fl \stop \quad
+ \start \LigatureFont ft \stop \quad
+ \start \LigatureFont ffb \stop \quad
+ \start \LigatureFont fff \stop \quad
+ \start \LigatureFont ffi \stop \quad
+ \start \LigatureFont ffl \stop \quad
+ \start \LigatureFont ffk \stop \quad
+ \start \LigatureFont fft \stop
+ \eTD
+ \eTR
+\stoptexdefinition
+
+\starttexdefinition showotfligaturesexample #1#2
+ \ctxlua{document.currentdiscexpansion = fonts.getdiscexpansion()}%
+ \showotfligaturescaptions {#1}
+ \showotfligatureslines {#2}
+ \showotfligaturesparagraphs{#2}
+ \showotfligaturesextremes {#2}
+ \ctxlua{fonts.setdiscexpansion(document.currentdiscexpansion)}%
+\stoptexdefinition
+
+% todo: n -> and split in lua
+
+\starttexdefinition showotfligaturesexamples
+ \showotfligaturesexample
+ {leafing,baffling,efficient,shifffahrt}
+ {leafing,baffling,efficient,shifffahrt}
+ \showotfligaturesexample
+ {offbeat,effect,ef-fective,ef\-fective}
+ {offbeat,effect,ef-fective,ef\-fective}
+ \showotfligaturesexample
+ {xf+fi+a,xff+i+b,xffi+c,xffid}
+ {xffia, xffib, xffic, xffid}
+\stoptexdefinition
+
+\starttexdefinition showotfligatures [#1]
+ \begingroup
+ \getdummyparameters[font=Regular,features=default,#1]
+ \definefont[LigatureFont][\dummyparameter{font}*\dummyparameter{features},otfligaturetest ht 2ex]
+ \bTABLE[leftframe=off,rightframe=off]
+ \showotfligaturesbanner{\dummyparameter{font} * \dummyparameter{features}}
+ \showotfligaturesdefaults
+ \showotfligaturesexamples
+ \eTABLE
+ \endgroup
+\stoptexdefinition
+
+\continueifinputfile{s-fonts-ligatures.mkiv}
+
+\starttext
+
+ \definefontfeature[fonttest][default] [mode=node,language=dflt,script=latn,ccmp=yes,liga=yes,kern=yes,mark=yes,mkmk=yes]
+ \definefontfeature[capstest][smallcaps][mode=node,language=dflt,script=latn,ccmp=yes,liga=yes,kern=yes,mark=yes,mkmk=yes]
+
+ \startTEXpage[offset=10pt] \showotfligatures[font=lmroman10-regular.otf, features=fonttest] \stopTEXpage
+ \startTEXpage[offset=10pt] \showotfligatures[font=dejavu-serif.ttf, features=fonttest] \stopTEXpage
+ \startTEXpage[offset=10pt] \showotfligatures[font=minionpro.otf, features=fonttest] \stopTEXpage
+ \startTEXpage[offset=10pt] \showotfligatures[font=minionpro.otf, features=capstest] \stopTEXpage
+ \startTEXpage[offset=10pt] \showotfligatures[font=brill.otf, features=fonttest] \stopTEXpage
+ \startTEXpage[offset=10pt] \showotfligatures[font=gentiumplus-r.ttf, features=fonttest] \stopTEXpage
+ \startTEXpage[offset=10pt] \showotfligatures[font=cambria, features=fonttest] \stopTEXpage
+ \startTEXpage[offset=10pt] \showotfligatures[font=ebgaramond12-regular.otf,features=fonttest] \stopTEXpage
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-fonts-missing.lua b/tex/context/modules/mkiv/s-fonts-missing.lua
new file mode 100644
index 000000000..9a75676a9
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-missing.lua
@@ -0,0 +1,91 @@
+if not modules then modules = { } end modules ['s-fonts-missing'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-missing.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.missing = moduledata.fonts.missing or { }
+
+local function legend(id)
+ local c = fonts.hashes.identifiers[id]
+ local privates = c.properties.privates
+ if privates then
+ local categories = table.swapped(fonts.loggers.category_to_placeholder)
+ context.starttabulate { "|c|l|" }
+ context.HL()
+ context.NC()
+ context.bold("symbol")
+ context.NC()
+ context.bold("name")
+ context.NC()
+ context.NR()
+ context.HL()
+ for k, v in table.sortedhash(privates) do
+ local tag = characters.categorytags[categories[k]]
+ if tag and tag ~= "" then
+ context.NC()
+ context.dontleavehmode()
+ context.char(v)
+ context.NC()
+ context(k)
+ context.NC()
+ context.NR()
+ end
+ end
+ context.HL()
+ context.stoptabulate()
+ end
+end
+
+function moduledata.fonts.missing.showlegend(specification)
+ specification = interfaces.checkedspecification(specification)
+ context.begingroup()
+ context.definedfont { "Mono*missing" } -- otherwise no privates added
+ context(function() legend(specification.id or font.current()) end)
+ context.endgroup()
+end
+
+local function missings()
+ local collected = fonts.checkers.getmissing()
+ for filename, list in table.sortedhash(collected) do
+ if #list > 0 then
+ context.starttabulate { "|l|l|" }
+ context.NC()
+ context.bold("filename")
+ context.NC()
+ context(file.basename(filename))
+ context.NC()
+ context.NR()
+ context.NC()
+ context.bold("missing")
+ context.NC()
+ context(#list)
+ context.NC()
+ context.NR()
+ context.stoptabulate()
+ context.starttabulate { "|l|c|l|" }
+ for i=1,#list do
+ local u = list[i]
+ context.NC()
+ context("%U",u)
+ context.NC()
+ context.char(u)
+ context.NC()
+ context(characters.data[u].description)
+ context.NC()
+ context.NR()
+ end
+ context.stoptabulate()
+ end
+ end
+end
+
+function moduledata.fonts.missing.showcharacters(specification)
+ context.begingroup()
+ context.definedfont { "Mono*missing" } -- otherwise no privates added
+ context(function() missings() end)
+ context.endgroup()
+end
diff --git a/tex/context/modules/mkiv/s-fonts-missing.mkiv b/tex/context/modules/mkiv/s-fonts-missing.mkiv
new file mode 100644
index 000000000..c566f4995
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-missing.mkiv
@@ -0,0 +1,56 @@
+%D \module
+%D [ file=s-fonts-missing,
+%D version=2011.10.30,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Some Missing Character Info,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[fonts-missing]
+
+\registerctxluafile{s-fonts-missing}{}
+
+\installmodulecommandluasingle \showmissingcharacterslegend {moduledata.fonts.missing.showlegend}
+\installmodulecommandluasingle \showmissingcharacters {moduledata.fonts.missing.showcharacters}
+
+\stopmodule
+
+\continueifinputfile{s-fonts-missing.mkiv}
+
+% the sooner, the more efficient, see log for details
+
+\enabletrackers[fonts.missing=replace]
+
+% \definefontfeature[default][default][missing=yes] % automatically when enabled
+
+\starttext
+
+ \page legend: \blank
+
+ \showmissingcharacterslegend
+
+ \page sample: \blank
+
+ ½ ⅓ ¼ ⅕ ⅙ ⅛ {\bf ½ ⅓ ¼ ⅕ ⅙ ⅛} \blank
+
+ \startluacode
+ for i=1,1000 do
+ local c = characters.data[i]
+ if c then
+ context.char(c.unicodeslot)
+ context.space()
+ end
+ end
+ \stopluacode
+
+ \page characters: \blank
+
+ \showmissingcharacters
+
+\stoptext
+
diff --git a/tex/context/modules/mkiv/s-fonts-shapes.lua b/tex/context/modules/mkiv/s-fonts-shapes.lua
new file mode 100644
index 000000000..8f872e4bc
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-shapes.lua
@@ -0,0 +1,326 @@
+if not modules then modules = { } end modules['s-fonts-shapes'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-shapes.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.shapes = moduledata.fonts.shapes or { }
+
+local fontdata = fonts.hashes.identifiers
+
+local context = context
+local NC, NR = context.NC, context.NR
+local space, dontleavehmode, glyph, getvalue = context.space, context.dontleavehmode, context.glyph, context.getvalue
+local formatters = string.formatters
+
+function char(id,k)
+ dontleavehmode()
+ glyph(id,k)
+end
+
+local function special(id,specials)
+ if specials and #specials > 1 then
+ context("%s:",specials[1])
+ if #specials > 5 then
+ space() char(id,specials[2])
+ space() char(id,specials[3])
+ space() context("...")
+ space() char(id,specials[#specials-1])
+ space() char(id,specials[#specials])
+ else
+ for i=2,#specials do
+ space() char(id,specials[i])
+ end
+ end
+ end
+end
+
+function moduledata.fonts.shapes.showlist(specification) -- todo: ranges
+ specification = interfaces.checkedspecification(specification)
+ local id, cs = fonts.definers.internal(specification,"<module:fonts:shapes:font>")
+ local chrs = fontdata[id].characters
+ context.begingroup()
+ context.tt()
+ context.starttabulate { "|l|c|c|c|c|l|l|" }
+ context.FL()
+ NC() context.bold("unicode")
+ NC() context.bold("glyph")
+ NC() context.bold("shape")
+ NC() context.bold("lower")
+ NC() context.bold("upper")
+ NC() context.bold("specials")
+ NC() context.bold("description")
+ NC() NR()
+ context.TL()
+ for k, v in next, characters.data do
+ if chrs[k] then
+ NC() context("0x%05X",k)
+ NC() char(id,k) -- getvalue(cs) context.char(k)
+ NC() char(id,v.shcode)
+ NC() char(id,v.lccode or k)
+ NC() char(id,v.uccode or k)
+ NC() special(id,v.specials)
+ NC() context.tx(v.description)
+ NC() NR()
+ end
+ end
+ context.stoptabulate()
+ context.endgroup()
+end
+
+function moduledata.fonts.shapes.showlist(specification) -- todo: ranges
+ specification = interfaces.checkedspecification(specification)
+ local id, cs = fonts.definers.internal(specification,"<module:fonts:shapes:font>")
+ local chrs = fontdata[id].characters
+ context.begingroup()
+ context.tt()
+ context.starttabulate { "|l|c|c|c|c|l|l|" }
+ context.FL()
+ NC() context.bold("unicode")
+ NC() context.bold("glyph")
+ NC() context.bold("shape")
+ NC() context.bold("lower")
+ NC() context.bold("upper")
+ NC() context.bold("specials")
+ NC() context.bold("description")
+ NC() NR()
+ context.TL()
+ for k, v in next, characters.data do
+ if chrs[k] then
+ NC() context("0x%05X",k)
+ NC() char(id,k)
+ NC() char(id,v.shcode)
+ NC() char(id,v.lccode or k)
+ NC() char(id,v.uccode or k)
+ NC() special(id,v.specials)
+ NC() context.tx(v.description)
+ NC() NR()
+ end
+ end
+ context.stoptabulate()
+ context.endgroup()
+end
+
+local descriptions = nil
+local characters = nil
+
+local function showglyphshape(specification)
+ specification = interfaces.checkedspecification(specification)
+ local id, cs = fonts.definers.internal(specification,"<module:fonts:shapes:font>")
+ local tfmdata = fontdata[id]
+ local charnum = tonumber(specification.character)
+ if not charnum then
+ charnum = fonts.helpers.nametoslot(n)
+ end
+ context.start()
+ context.dontleavehmode()
+ context.obeyMPboxdepth()
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local parameters = tfmdata.parameters
+ local c = characters[charnum]
+ local d = descriptions[charnum]
+ if d then
+ local factor = (parameters.size/parameters.units)*((7200/7227)/65536)
+ local llx, lly, urx, ury = unpack(d.boundingbox)
+ llx, lly, urx, ury = llx*factor, lly*factor, urx*factor, ury*factor
+ local width, italic = (d.width or 0)*factor, (d.italic or 0)*factor
+ local top_accent, bot_accent = (d.top_accent or 0)*factor, (d.bot_accent or 0)*factor
+ local anchors, math = d.anchors, d.math
+ context.startMPcode()
+ context("numeric lw ; lw := .125bp ;")
+ context("pickup pencircle scaled lw ;")
+ context('picture p ; p := image(draw textext.drt("\\getuvalue{%s}\\gray\\char%s");); draw p ;',cs,charnum)
+ context('draw (%s,%s)--(%s,%s)--(%s,%s)--(%s,%s)--cycle withcolor green ;',llx,lly,urx,lly,urx,ury,llx,ury)
+ context('draw (%s,%s)--(%s,%s) withcolor green ;',llx,0,urx,0)
+ context('draw boundingbox p withcolor .2white withpen pencircle scaled .065bp ;')
+ context("defaultscale := 0.05 ; ")
+ -- inefficient but non critical
+ local function slant_1(v,dx,dy,txt,xsign,ysign,loc,labloc)
+ if #v > 0 then
+ local l = { }
+ for kk, vv in ipairs(v) do
+ local h, k = vv.height, vv.kern
+ if h and k then
+ l[#l+1] = formatters["((%s,%s) shifted (%s,%s))"](xsign*k*factor,ysign*h*factor,dx,dy)
+ end
+ end
+ context("draw ((%s,%s) shifted (%s,%s))--%s dashed (evenly scaled 1/16) withcolor .5white;", xsign*v[1].kern*factor,lly,dx,dy,l[1])
+ context("draw laddered (%s) withcolor .5white ;",table.concat(l,".."))
+ context("draw ((%s,%s) shifted (%s,%s))--%s dashed (evenly scaled 1/16) withcolor .5white;", xsign*v[#v].kern*factor,ury,dx,dy,l[#l])
+ for k, v in ipairs(l) do
+ context("draw %s withcolor blue withpen pencircle scaled 2lw ;",v)
+ end
+ end
+ end
+ local function slant_2(v,dx,dy,txt,xsign,ysign,loc,labloc)
+ if #v > 0 then
+ local l = { }
+ for kk, vv in ipairs(v) do
+ local h, k = vv.height, vv.kern
+ if h and k then
+ l[#l+1] = formatters["((%s,%s) shifted (%s,%s))"](xsign*k*factor,ysign*h*factor,dx,dy)
+ end
+ end
+ if loc == "top" then
+ context('label.%s("\\type{%s}",%s shifted (0,-1bp)) ;',loc,txt,l[#l])
+ else
+ context('label.%s("\\type{%s}",%s shifted (0,2bp)) ;',loc,txt,l[1])
+ end
+ for kk, vv in ipairs(v) do
+ local h, k = vv.height, vv.kern
+ if h and k then
+ context('label.top("(%s,%s)",%s shifted (0,-2bp));',k,h,l[kk])
+ end
+ end
+ end
+ end
+ if math then
+ local kerns = math.kerns
+ if kerns then
+ for _, slant in ipairs { slant_1, slant_2 } do
+ for k,v in pairs(kerns) do
+ if k == "top_right" then
+ slant(v,width+italic,0,k,1,1,"top","ulft")
+ elseif k == "bottom_right" then
+ slant(v,width,0,k,1,1,"bot","lrt")
+ elseif k == "top_left" then
+ slant(v,0,0,k,-1,1,"top","ulft")
+ elseif k == "bottom_left" then
+ slant(v,0,0,k,-1,1,"bot","lrt")
+ end
+ end
+ end
+ end
+ end
+ local function show(x,y,txt)
+ local xx, yy = x*factor, y*factor
+ context("draw (%s,%s) withcolor blue withpen pencircle scaled 2lw ;",xx,yy)
+ context('label.top("\\type{%s}",(%s,%s-2bp)) ;',txt,xx,yy)
+ context('label.bot("(%s,%s)",(%s,%s+2bp)) ;',x,y,xx,yy)
+ end
+ if anchors then
+ local a = anchors.baselig
+ if a then
+ for k, v in pairs(a) do
+ for kk, vv in ipairs(v) do
+ show(vv[1],vv[2],k .. ":" .. kk)
+ end
+ end
+ end
+ local a = anchors.mark
+ if a then
+ for k, v in pairs(a) do
+ show(v[1],v[2],k)
+ end
+ end
+ local a = anchors.basechar
+ if a then
+ for k, v in pairs(a) do
+ show(v[1],v[2],k)
+ end
+ end
+ local ba = anchors.centry
+ if a then
+ for k, v in pairs(a) do
+ show(v[1],v[2],k)
+ end
+ end
+ local a = anchors.cexit
+ if a then
+ for k, v in pairs(a) do
+ show(v[1],v[2],k)
+ end
+ end
+ end
+ if italic ~= 0 then
+ context('draw (%s,%s-1bp)--(%s,%s-0.5bp) withcolor blue ;',width,ury,width,ury)
+ context('draw (%s,%s-1bp)--(%s,%s-0.5bp) withcolor blue ;',width+italic,ury,width+italic,ury)
+ context('draw (%s,%s-1bp)--(%s,%s-1bp) withcolor blue ;',width,ury,width+italic,ury)
+ context('label.lft("\\type{%s}",(%s+2bp,%s-1bp));',"italic",width,ury)
+ context('label.rt("%s",(%s-2bp,%s-1bp));',d.italic,width+italic,ury)
+ end
+ if top_accent ~= 0 then
+ context('draw (%s,%s+1bp)--(%s,%s-1bp) withcolor blue;',top_accent,ury,top_accent,ury)
+ context('label.bot("\\type{%s}",(%s,%s+1bp));',"top_accent",top_accent,ury)
+ context('label.top("%s",(%s,%s-1bp));',d.top_accent,top_accent,ury)
+ end
+ if bot_accent ~= 0 then
+ context('draw (%s,%s+1bp)--(%s,%s-1bp) withcolor blue;',bot_accent,lly,bot_accent,lly)
+ context('label.top("\\type{%s}",(%s,%s-1bp));',"bot_accent",top_accent,ury)
+ context('label.bot("%s",(%s,%s+1bp));',d.bot_accent,bot_accent,lly)
+ end
+ context('draw origin withcolor red withpen pencircle scaled 2lw;')
+ context("setbounds currentpicture to boundingbox currentpicture enlarged 1bp ;")
+ context("currentpicture := currentpicture scaled 8 ;")
+ context.stopMPcode()
+ -- elseif c then
+ -- lastdata, lastunicode = nil, nil
+ -- local factor = (7200/7227)/65536
+ -- context.startMPcode()
+ -- context("pickup pencircle scaled .25bp ; ")
+ -- context('picture p ; p := image(draw textext.drt("\\gray\\char%s");); draw p ;',charnum)
+ -- context('draw boundingbox p withcolor .2white withpen pencircle scaled .065bp ;')
+ -- context("defaultscale := 0.05 ; ")
+ -- local italic, top_accent, bot_accent = (c.italic or 0)*factor, (c.top_accent or 0)*factor, (c.bot_accent or 0)*factor
+ -- local width, height, depth = (c.width or 0)*factor, (c.height or 0)*factor, (c.depth or 0)*factor
+ -- local ury = height
+ -- if italic ~= 0 then
+ -- context('draw (%s,%s-1bp)--(%s,%s-0.5bp) withcolor blue;',width,ury,width,ury)
+ -- context('draw (%s,%s-1bp)--(%s,%s-0.5bp) withcolor blue;',width+italic,ury,width+italic,ury)
+ -- context('draw (%s,%s-1bp)--(%s,%s-1bp) withcolor blue;',width,ury,width+italic,height)
+ -- context('label.lft("\\type{%s}",(%s+2bp,%s-1bp));',"italic",width,height)
+ -- context('label.rt("%6.3f bp",(%s-2bp,%s-1bp));',italic,width+italic,height)
+ -- end
+ -- if top_accent ~= 0 then
+ -- context('draw (%s,%s+1bp)--(%s,%s-1bp) withcolor blue;',top_accent,ury,top_accent,height)
+ -- context('label.bot("\\type{%s}",(%s,%s+1bp));',"top_accent",top_accent,height)
+ -- context('label.top("%6.3f bp",(%s,%s-1bp));',top_accent,top_accent,height)
+ -- end
+ -- if bot_accent ~= 0 then
+ -- context('draw (%s,%s+1bp)--(%s,%s-1bp) withcolor blue;',bot_accent,lly,bot_accent,height)
+ -- context('label.top("\\type{%s}",(%s,%s-1bp));',"bot_accent",top_accent,height)
+ -- context('label.bot("%6.3f bp",(%s,%s+1bp));',bot_accent,bot_accent,height)
+ -- end
+ -- context('draw origin withcolor red withpen pencircle scaled 1bp;')
+ -- context("setbounds currentpicture to boundingbox currentpicture enlarged 1bp ;")
+ -- context("currentpicture := currentpicture scaled 8 ;")
+ -- context.stopMPcode()
+ else
+ lastdata, lastunicode = nil, nil
+ context("no such shape: 0x%05X",charnum)
+ end
+ context.stop()
+end
+
+moduledata.fonts.shapes.showglyphshape = showglyphshape
+
+function moduledata.fonts.shapes.showallglypshapes(specification)
+ specification = interfaces.checkedspecification(specification)
+ local id, cs = fonts.definers.internal(specification,"<module:fonts:shapes:font>")
+ local descriptions = fontdata[id].descriptions
+ for unicode, description in fonts.iterators.descriptions(tfmdata) do
+ if unicode >= 0x110000 then
+ break
+ end
+ context.modulefontsstartshowglyphshape(unicode,description.name or "",description.index or 0)
+ showglyphshape { number = id, character = unicode }
+ context.modulefontsstopshowglyphshape()
+ end
+end
+
+function moduledata.fonts.shapes.showlastglyphshapefield(unicode,name)
+ if not descriptions then
+ -- bad news
+ elseif name == "unicode" then
+ context("U+%05X",descriptions.unicode)
+ else
+ local d = descriptions[name]
+ if d then
+ context(d)
+ end
+ end
+end
diff --git a/tex/context/modules/mkiv/s-fonts-shapes.mkiv b/tex/context/modules/mkiv/s-fonts-shapes.mkiv
new file mode 100644
index 000000000..c1e9d61d2
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-shapes.mkiv
@@ -0,0 +1,115 @@
+%D \module
+%D [ file=s-fonts-shapes, % s-fnt-29,
+%D version=2010.09.27,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Tracing Shapes,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[fonts-shapes]
+
+\registerctxluafile{s-fonts-shapes}{}
+
+\installmodulecommandluasingle \showfontshapes {moduledata.fonts.shapes.showlist}
+\installmodulecommandluasingle \showglyphshape {moduledata.fonts.shapes.showglypshape}
+\installmodulecommandluatwo \showlastglyphshapefield {moduledata.fonts.shapes.showlastglyphshapefield}
+\installmodulecommandluasingle \showallglyphshapes {moduledata.fonts.shapes.showallglypshapes}
+
+\let\modulefontsstartshowglyphshape\gobblethreearguments
+\let\modulefontsstopshowglyphshape \relax
+
+\unprotect
+
+\startsetups module:showallglyphshapes:start
+
+ \unexpanded\def\modulefontsstartshowglyphshape##1##2##3{
+ \startTEXpage[\c!offset=\exheight]
+ \edef\lastshownglyphshapefieldunicode{##1}%
+ \edef\lastshownglyphshapefieldname {##2}%
+ \edef\lastshownglyphshapefieldindex {##3}%
+ \raggedcenter
+ }
+
+ \unexpanded\def\modulefontsstopshowglyphshape {
+ \par
+ \doifsomething {\lastshownglyphshapefieldunicode} {
+ \begingroup
+ \dontleavehmode
+ \tttf
+ \setstrut
+ \strut
+ 0x\uchexnumbers\lastshownglyphshapefieldunicode
+ \space:\space
+ \lastshownglyphshapefieldname
+ \space:\space
+ \lastshownglyphshapefieldindex
+ \par
+ \endgroup
+ }
+ \stopTEXpage
+ }
+
+\stopsetups
+
+\protect
+
+% downward compatibility:
+
+\unexpanded\def\ShowGlyphShape#1#2#3% name size glyph
+ {\ctxlua{moduledata.fonts.shapes.showglyphshape { name = "#1", size = "#2", character = "#3" } }}
+
+\stopmodule
+
+\continueifinputfile{s-fonts-shapes.mkiv}
+
+\setupbodyfont[dejavu,9pt]
+
+\setuplayout
+ [backspace=1cm,
+ topspace=1cm,
+ footer=1cm,
+ header=0cm,
+ height=middle,
+ width=middle]
+
+\setupfootertexts
+ []
+ [\saveddefinedfontname\quad\pagenumber]
+
+\starttext
+
+% \savedefinedfont[Bold*default]
+% \showfontshapes[number=\saveddefinedfontid]
+% \page
+
+% \showfontshapes[name=BoldItalic*default]
+% \page
+
+ % \startTEXpage \ShowGlyphShape{simplenaskhi}{100bp}{0x62A} \stopTEXpage
+ % \startTEXpage \ShowGlyphShape{simplenaskhi}{100bp}{0x2004} \stopTEXpage
+ % \startTEXpage \ShowGlyphShape{simplenaskhi}{100bp}{0xF0299} \stopTEXpage
+ % \startTEXpage \ShowGlyphShape{simplenaskhi}{100bp}{NameMe.1190} \stopTEXpage
+
+% \startTEXpage[offset=0pt]\ShowGlyphShape{name:cambria-math}{20bp}{0x00066}\stopTEXpage
+% \startTEXpage[offset=0pt]\ShowGlyphShape{name:cambria-math}{20bp}{0x1D453}\stopTEXpage
+% \startTEXpage[offset=0pt]\ShowGlyphShape{name:cambria-math}{20bp}{0x1D43F}\stopTEXpage
+% \startTEXpage[offset=0pt]\ShowGlyphShape{name:cambria-math}{50bp}{0x1D444}\stopTEXpage
+% \startTEXpage[offset=0pt]\ShowGlyphShape{name:cambria-math}{50bp}{0x1D447}\stopTEXpage
+% \startTEXpage[offset=0pt]\ShowGlyphShape{name:cambria-math}{50bp}{0x02112}\stopTEXpage
+% \startTEXpage[offset=0pt]\ShowGlyphShape{name:cambria-math}{50bp}{0x1D432}\stopTEXpage
+% \startTEXpage[offset=0pt]\ShowGlyphShape{name:cambria-math}{50bp}{0x1D43D}\stopTEXpage
+% \startTEXpage[offset=0pt]\ShowGlyphShape{name:cambria-math}{50bp}{0x1D44A}\stopTEXpage
+% \startTEXpage[offset=0pt]\ShowGlyphShape{name:cambria-math}{50bp}{0x1D45D}\stopTEXpage
+% \page
+
+ % \showallglyphshapes[name=name:cambria-math,size=100bp]
+% \showallglyphshapes[name=name:dejavuserif,size=100bp]
+
+\showallglyphshapes[name=file:brill.otf,size=100bp]
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-fonts-system.lua b/tex/context/modules/mkiv/s-fonts-system.lua
new file mode 100644
index 000000000..0c0ad4d86
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-system.lua
@@ -0,0 +1,68 @@
+if not modules then modules = { } end modules ['s-fonts-system'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-system.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- ["zapfinoforteltpro"]={
+-- ["designsize"]=0,
+-- ["familyname"]="zapfinoforteltpro",
+-- ["filename"]="zapfinoforteltpro.otf",
+-- ["fontname"]="zapfinoforteltpro",
+-- ["fontweight"]="regular",
+-- ["format"]="otf",
+-- ["fullname"]="zapfinoforteltpro",
+-- ["maxsize"]=0,
+-- ["minsize"]=0,
+-- ["modification"]=1105543074,
+-- ["modifiers"]="regular",
+-- ["rawname"]="ZapfinoForteLTPro",
+-- ["style"]="normal",
+-- ["subfamily"]="regular",
+-- ["variant"]="normal",
+-- ["weight"]="normal",
+-- ["width"]="normal",
+-- }
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.system = moduledata.fonts.system or { }
+
+local lower = string.lower
+
+local context = context
+local NC, NR, HL = context.NC, context.NR, context.HL
+local bold = context.bold
+
+function moduledata.fonts.system.showinstalled(specification)
+ specification = interfaces.checkedspecification(specification)
+ local pattern = lower(specification.pattern or "")
+ local list = fonts.names.list(pattern,false,true)
+ if list then
+ local files = { }
+ for k, v in next, list do
+ files[file.basename(string.lower(v.filename))] = v
+ end
+ context.starttabulate { "|Tl|Tl|Tl|Tl|Tl|Tl|" }
+ HL()
+ NC() bold("filename")
+ NC() bold("fontname")
+ NC() bold("subfamily")
+ NC() bold("variant")
+ NC() bold("weight")
+ NC() bold("width")
+ NC() NR()
+ HL()
+ for filename, data in table.sortedpairs(files) do
+ NC() context(filename)
+ NC() context(data.fontname)
+ NC() context(data.subfamily)
+ NC() context(data.variant)
+ NC() context(data.weight)
+ NC() context(data.width)
+ NC() NR()
+ end
+ context.stoptabulate()
+ end
+end
diff --git a/tex/context/modules/mkiv/s-fonts-system.mkiv b/tex/context/modules/mkiv/s-fonts-system.mkiv
new file mode 100644
index 000000000..6d9082a6b
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-system.mkiv
@@ -0,0 +1,39 @@
+%D \module
+%D [ file=s-fonts0system, % s-fnt-11,
+%D version=2006.02.01, % or so
+%D title=\CONTEXT\ Style File,
+%D subtitle=Listing Installed Fonts,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This code usd to be in the kernel but since it's hardly used
+%D it's now a module.
+%D
+%D \starttyping
+%D \showinstalledfonts[pattern={officinasans.*}]
+%D \showinstalledfonts[pattern={officinaserif.*}]
+%D \showinstalledfonts[pattern={officina.*itc.*}]
+%D \stoptyping
+
+\startmodule[fonts-system]
+
+\registerctxluafile{s-fonts-system}{}
+
+\installmodulecommandluasingle \showinstalledfonts {moduledata.fonts.system.showinstalled}
+
+\stopmodule
+
+\continueifinputfile{s-fonts-system.mkiv}
+
+\usemodule[art-01] \setuplayout[overview] \setupbodyfont[7pt]
+
+\starttext
+
+ \showinstalledfonts
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-fonts-tables.lua b/tex/context/modules/mkiv/s-fonts-tables.lua
new file mode 100644
index 000000000..c32f4628c
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-tables.lua
@@ -0,0 +1,363 @@
+if not modules then modules = { } end modules ['s-fonts-tables'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-tables.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.tables = moduledata.fonts.tables or { }
+
+local setmetatableindex = table.setmetatableindex
+local sortedhash = table.sortedhash
+local sortedkeys = table.sortedkeys
+local format = string.format
+local concat = table.concat
+
+local tabletracers = moduledata.fonts.tables
+
+local context = context
+
+local digits = {
+ dflt = {
+ dflt = "1234567890 1/2",
+ },
+ arab = {
+ dflt = "",
+ },
+ latn = {
+ dflt = "1234567890 1/2",
+ }
+}
+
+local punctuation = {
+ dflt = {
+ dflt = ". , : ; ? ! ‹ › « »",
+ },
+}
+
+local symbols = {
+ dflt = {
+ dflt = "@ # $ % & * () [] {} <> + - = / |",
+ },
+}
+
+local LATN = "abcdefghijklmnopqrstuvwxyz"
+
+local uppercase = {
+ latn = {
+ dflt = LATN,
+ fra = LATN .. " ÀÁÂÈÉÊÒÓÔÙÚÛÆÇ",
+ },
+ grek = {
+ dftl = "ΑΒΓΔΕΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ",
+ },
+ cyrl= {
+ dflt = "АБВГДЕЖЗИІЙКЛМНОПРСТУФХЦЧШЩЪЫЬѢЭЮЯѲ"
+ },
+}
+
+local latn = "abcdefghijklmnopqrstuvwxyz"
+
+local lowercase = {
+ latn = {
+ dftl = latn,
+ nld = latn .. " ïèéë",
+ deu = latn .. " äöüß",
+ fra = latn .. " àáâèéêòóôùúûæç",
+ },
+ grek = {
+ dftl = "αβγδεηθικλμνξοπρστυφχψω",
+ },
+ cyrl= {
+ dflt = "абвгдежзиійклмнопрстуфхцчшщъыьѣэюяѳ"
+ },
+ arab = {
+ dflt = "ابجدهوزحطيكلمنسعفصقرشتثخذضظغ"
+ },
+}
+
+local samples = {
+ digits = digits,
+ punctuation = punctuation,
+ symbols = symbols,
+ uppercase = uppercase,
+ lowercase = lowercase,
+}
+
+tabletracers.samples = samples
+
+setmetatableindex(uppercase, function(t,k) return rawget(t,"latn") end)
+setmetatableindex(lowercase, function(t,k) return rawget(t,"latn") end)
+setmetatableindex(digits, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(symbols, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(punctuation, function(t,k) return rawget(t,"dflt") end)
+
+setmetatableindex(uppercase.latn, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(uppercase.grek, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(uppercase.cyrl, function(t,k) return rawget(t,"dflt") end)
+
+setmetatableindex(lowercase.latn, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(lowercase.grek, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(lowercase.cyrl, function(t,k) return rawget(t,"dflt") end)
+
+setmetatableindex(digits.dflt, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(symbols.dflt, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(punctuation.dflt, function(t,k) return rawget(t,"dflt") end)
+
+local function typesettable(t,keys,synonyms,nesting,prefix)
+ if t then
+ if not prefix then
+ context.starttabulate { "|Tl|Tl|Tl|" }
+ end
+ for k, v in sortedhash(keys) do
+ if k == "synonyms" then
+ elseif type(v) ~= "table" then
+ context.NC()
+ if prefix then
+ context("%s.%s",prefix,k)
+ else
+ context(k)
+ end
+ context.NC()
+ local tk = t[k]
+ if v == "boolean" then
+ context(tostring(tk or false))
+ elseif not tk then
+ context("<unset>")
+ elseif v == "filename" then
+ context(file.basename(tk))
+ elseif v == "basepoints" then
+ context("%sbp",tk)
+ elseif v == "scaledpoints" then
+ context("%p",tk)
+ elseif v == "table" then
+ context("<table>")
+ else -- if v == "integerscale" then
+ context(tostring(tk))
+ end
+ context.NC()
+ local synonym = (not prefix and synonyms[k]) or (prefix and synonyms[format("%s.%s",prefix,k)])
+ if synonym then
+ context(format("(%s)",concat(synonym," ")))
+ end
+ context.NC()
+ context.NR()
+ elseif nesting == false then
+ context("<table>")
+ else -- true or nil
+ typesettable(t[k],v,synonyms,nesting,k)
+ end
+ end
+ if not prefix then
+ context.stoptabulate()
+ end
+ end
+end
+
+local function typeset(t,keys,nesting,prefix)
+ local synonyms = keys.synonyms or { }
+ local collected = { }
+ for k, v in next, synonyms do
+ local c = collected[v]
+ if not c then
+ c = { }
+ collected[v] = c
+ end
+ c[#c+1] = k
+ end
+ for k, v in next, collected do
+ table.sort(v)
+ end
+ typesettable(t,keys,collected,nesting,prefix)
+end
+
+tabletracers.typeset = typeset
+
+function tabletracers.showproperties(nesting)
+ local tfmdata = fonts.hashes.identifiers[font.current()]
+ typeset(tfmdata.properties,fonts.constructors.keys.properties,nesting)
+end
+
+function tabletracers.showparameters(nesting)
+ local tfmdata = fonts.hashes.identifiers[font.current()]
+ typeset(tfmdata.parameters,fonts.constructors.keys.parameters,nesting)
+end
+
+function tabletracers.showpositionings()
+ local tfmdata = fonts.hashes.resources[font.current()]
+ local resources = tfmdata.resources
+ if resources then
+ local features = resources.features
+ if features then
+ local gpos = features.gpos
+ if gpos and next(gpos) then
+ context.starttabulate { "|Tl|Tl|Tlp|" }
+ for feature, scripts in sortedhash(gpos) do
+ for script, languages in sortedhash(scripts) do
+ context.NC()
+ context(feature)
+ context.NC()
+ context(script)
+ context.NC()
+ context(concat(sortedkeys(languages)," "))
+ context.NC()
+ context.NR()
+ end
+ end
+ context.stoptabulate()
+ else
+ context("no entries")
+ context.par()
+ end
+ end
+ end
+end
+
+local dynamics = true
+
+function tabletracers.showsubstitutions()
+ local tfmdata = fonts.hashes.identifiers[font.current()]
+ local resources = tfmdata.resources
+ if resources then
+ local features = resources.features
+ if features then
+ local gsub = features.gsub
+ if gsub then
+ local makes_sense = { }
+ for feature, scripts in sortedhash(gsub) do
+ for script, languages in sortedhash(scripts) do
+ for language in sortedhash(languages) do
+ local tag = format("dummy-%s-%s-%s",feature,script,language)
+ local fnt = format("file:%s*%s",file.basename(tfmdata.properties.filename),tag)
+ context.definefontfeature (
+ { tag },
+ {
+ mode = "node",
+ script = script,
+ language = language,
+ [feature] = "yes"
+ }
+ )
+ if not dynamics then
+ context.definefont( { fnt }, { fnt } )
+ end
+ makes_sense[#makes_sense+1] = {
+ feature = feature,
+ tag = tag,
+ script = script,
+ language = language,
+ fontname = fnt,
+ }
+ end
+ end
+ end
+ if #makes_sense > 0 then
+ context.starttabulate { "|Tl|Tl|Tl|p|" }
+ for i=1,#makes_sense do
+ local data = makes_sense[i]
+ local script = data.script
+ local language = data.language
+ context.NC()
+ context(data.feature)
+ context.NC()
+ context(script)
+ context.NC()
+ context(language)
+ context.NC()
+ if not dynamics then
+ context.startfont { data.fontname }
+ else
+ context.addff(data.tag)
+ end
+ context.verbatim(samples.lowercase [script][language]) context.par()
+ context.verbatim(samples.uppercase [script][language]) context.par()
+ context.verbatim(samples.digits [script][language]) context.par()
+ context.verbatim(samples.punctuation[script][language]) context.quad()
+ context.verbatim(samples.symbols [script][language])
+ if not dynamics then
+ context.stopfont()
+ end
+ context.NC()
+ context.NR()
+ end
+ context.stoptabulate()
+ else
+ context("no entries")
+ context.par()
+ end
+ end
+ end
+ end
+end
+
+function tabletracers.showunicodevariants()
+
+ local variants = fonts.hashes.variants[true]
+
+ if variants then
+ context.starttabulate { "|c|c|c|c|c|c|c|" }
+ for selector, unicodes in sortedhash(variants) do
+ local done = false
+ for unicode, variant in sortedhash(unicodes) do
+ context.NC()
+ if not done then
+ context("%U",selector)
+ done = true
+ end
+ context.NC()
+ context("%U",unicode)
+ context.NC()
+ context("%c",unicode)
+ context.NC()
+ context("%U",variant)
+ context.NC()
+ context("%c",variant)
+ context.NC()
+ context("%c%c",unicode,selector)
+ context.NC()
+ context.startoverlay()
+ context("{\\color[trace:r]{%c}}{\\color[trace:ds]{%c}}",unicode,variant)
+ context.stopoverlay()
+ context.NC()
+ context.NR()
+ end
+ end
+ context.stoptabulate()
+ end
+
+end
+function tabletracers.showall(specification) -- not interfaced
+
+ specification = interfaces.checkedspecification(specification)
+
+ if specification.title then
+ context.starttitle { title = specification.title }
+ end
+
+ context.startsubject { title = "Properties" }
+ tabletracers.showproperties()
+ context.stopsubject()
+
+ context.startsubject { title = "Parameters" }
+ tabletracers.showparameters()
+ context.stopsubject()
+
+ context.startsubject { title = "Positioning features" }
+ tabletracers.showpositionings()
+ context.stopsubject()
+
+ context.startsubject { title = "Substitution features" }
+ tabletracers.showsubstitutions()
+ context.stopsubject()
+
+ context.startsubject { title = "Unicode variants" }
+ tabletracers.showunicodevariants()
+ context.stopsubject()
+
+ if title then
+ context.stoptitle()
+ end
+
+end
diff --git a/tex/context/modules/mkiv/s-fonts-tables.mkiv b/tex/context/modules/mkiv/s-fonts-tables.mkiv
new file mode 100644
index 000000000..64fe76f0e
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-tables.mkiv
@@ -0,0 +1,38 @@
+%D \module
+%D [ file=s-fonts-tables,
+%D version=2011.10.10,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Basic Font Data Tables,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% todo: make a mtxrun --script font <name> option
+
+\startmodule[fonts-tables]
+
+\registerctxluafile{s-fonts-tables}{}
+
+\installmodulecommandluasingle \showfonttables {moduledata.fonts.tables.showall}
+\installmodulecommandluasingle \showfontproperties {moduledata.fonts.tables.showproperties}
+\installmodulecommandluasingle \showfontparameters {moduledata.fonts.tables.showparameters}
+\installmodulecommandluasingle \showfontpositionings {moduledata.fonts.tables.showpositionings}
+\installmodulecommandluasingle \showfontsubstitutions {moduledata.fonts.tables.showsubstitutions}
+\installmodulecommandluasingle \showfontunicodevariants{moduledata.fonts.tables.showunicodevariants}
+
+\stopmodule
+
+\continueifinputfile{s-fonts-tables.mkiv}
+
+\usemodule[art-01]
+
+\setupbodyfont
+ [cambria]
+
+\starttext
+ \showfonttables[title=Cambria]
+\stoptext
diff --git a/tex/context/modules/mkiv/s-fonts-vectors.lua b/tex/context/modules/mkiv/s-fonts-vectors.lua
new file mode 100644
index 000000000..af8042f84
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-vectors.lua
@@ -0,0 +1,104 @@
+if not modules then modules = { } end modules ['s-fonts-vectors'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-vectors.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.protrusions = moduledata.fonts.protrusions or { }
+moduledata.fonts.expansions = moduledata.fonts.expansions or { }
+
+local NC, NR = context.NC, context.NR
+
+local classes = fonts.protrusions.classes
+local vectors = fonts.protrusions.vectors
+
+function moduledata.fonts.protrusions.showvector(specification)
+ specification = interfaces.checkedspecification(specification)
+ local vector = vectors[specification.name or "?"]
+ if vector then
+ context.blank()
+ context.startcolumns { n = specification.columns or 3, balance="yes" }
+ context.starttabulate { "|T||cw(.5em)||" }
+ for unicode, values in table.sortedhash(vector) do
+ NC() context("%U",unicode)
+ NC() context("%.02f",values[1])
+ NC() context("%c",unicode)
+ NC() context("%.02f",values[2])
+ NC() NR()
+ end
+ context.stoptabulate()
+ context.stopcolumns()
+ context.blank()
+ end
+end
+
+function moduledata.fonts.protrusions.showclass(specification)
+ specification = interfaces.checkedspecification(specification)
+ local class = specification.name and classes[specification.name]
+ local classes = class and { class} or classes
+ context.starttabulate { "|l|l|r|r|r|" }
+ NC() context.bold("name")
+ NC() context.bold("vector")
+ NC() context.bold("factor")
+ NC() context.bold("left")
+ NC() context.bold("right")
+ NC() NR()
+ for name, class in table.sortedhash(classes) do
+ NC() context(name)
+ NC() context(class.vector)
+ NC() context("%.02f",class.factor)
+ NC() context("%.02f",class.left)
+ NC() context("%.02f",class.right)
+ NC() NR()
+ end
+ context.stoptabulate()
+end
+
+local classes = fonts.expansions.classes
+local vectors = fonts.expansions.vectors
+
+function moduledata.fonts.expansions.showvector(specification)
+ specification = interfaces.checkedspecification(specification)
+ local vector = vectors[specification.name or "?"]
+ if vector then
+ context.blank()
+ context.startcolumns { n = specification.columns or 3, balance="yes" }
+ context.starttabulate { "|T|cw(.5em)||" }
+ for unicode, value in table.sortedhash(vector) do
+ NC() context("%U",unicode)
+ NC() context("%c",unicode)
+ NC() context("%.02f",value)
+ NC() NR()
+ end
+ context.stoptabulate()
+ context.stopcolumns()
+ context.blank()
+ end
+end
+
+function moduledata.fonts.expansions.showclass(specification)
+ specification = interfaces.checkedspecification(specification)
+ local class = specification.name and classes[specification.name]
+ local classes = class and { class} or classes
+ context.starttabulate { "|l|l|r|r|r|" }
+ NC() context.bold("name")
+ NC() context.bold("vector")
+ NC() context.bold("step")
+ NC() context.bold("factor")
+ NC() context.bold("stretch")
+ NC() context.bold("shrink")
+ NC() NR()
+ for name, class in table.sortedhash(classes) do
+ NC() context(name)
+ NC() context(class.vector)
+ NC() context("%.02f",class.step)
+ NC() context("%.02f",class.factor)
+ NC() context("% 2i",class.stretch)
+ NC() context("% 2i",class.shrink)
+ NC() NR()
+ end
+ context.stoptabulate()
+end
diff --git a/tex/context/modules/mkiv/s-fonts-vectors.mkiv b/tex/context/modules/mkiv/s-fonts-vectors.mkiv
new file mode 100644
index 000000000..2605fe964
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-vectors.mkiv
@@ -0,0 +1,72 @@
+%D \module
+%D [ file=s-fonts-vectors, % was s-fnt-51,
+%D version=2012.11.27,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Protrusion and Expansion,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This code is used in the \MKIV\ fonts manual.
+
+\startmodule[fonts-vectors]
+
+\registerctxluafile{s-fonts-vectors}{}
+
+\installmodulecommandluasingle \showprotrusionclass {moduledata.fonts.protrusions.showclass}
+\installmodulecommandluasingle \showprotrusionvector {moduledata.fonts.protrusions.showvector}
+\installmodulecommandluasingle \showexpansionclass {moduledata.fonts.expansions.showclass}
+\installmodulecommandluasingle \showexpansionvector {moduledata.fonts.expansions.showvector}
+
+\stopmodule
+
+\continueifinputfile{s-fonts-vectors.mkiv}
+
+\usemodule[art-01]
+
+\starttext
+
+ \startsubject[title=protrusion]
+
+ \startsubsubject[title=class]
+ \showprotrusionclass
+ \stopsubsubject
+
+ \startsubsubject[title=vector pure]
+ \showprotrusionvector[name=pure]
+ \stopsubsubject
+
+ \startsubsubject[title=vector punctuation]
+ \showprotrusionvector[name=punctuation]
+ \stopsubsubject
+
+ \startsubsubject[title=vector alpha]
+ \showprotrusionvector[name=alpha]
+ \stopsubsubject
+
+ \startsubsubject[title=vector quality]
+ \showprotrusionvector[name=quality]
+ \stopsubsubject
+
+ \stopsubject
+
+ \page
+
+ \startsubject[title=expansion]
+
+ \startsubsubject[title=class]
+ \showprotrusionclass
+ \stopsubsubject
+
+ \startsubsubject[title=vector quality]
+ \showprotrusionvector[name=quality]
+ \stopsubsubject
+
+ \stopsubject
+
+\stoptext
+
diff --git a/tex/context/modules/mkiv/s-inf-01.mkvi b/tex/context/modules/mkiv/s-inf-01.mkvi
new file mode 100644
index 000000000..2c0c1681e
--- /dev/null
+++ b/tex/context/modules/mkiv/s-inf-01.mkvi
@@ -0,0 +1,260 @@
+%D \module
+%D [ file=s-inf-01,
+%D version=2009.07.09,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Information 1 (\MKII/\MKIV\ usage),
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Some day I will generalize this table mechanism. This list is only
+%D right when run in the minimals as my machine might have more files.
+%D
+%D Als, the remaining tex code can proably be lua also which is more
+%D consistent.
+%D
+%D \starttyping
+%D context auto:s-inf-01
+%D context auto:s-inf-01 --basepath=t:/texmf/tex/context/base
+%D \stoptyping
+
+% \enabletrackers[context.*]
+
+\startluacode
+ local format, gsub, find, match = string.format, string.gsub, string.find, string.match
+
+ local list, size, comp, used, nope = { }, { }, { }, { mkii = { }, mkiv = { }, mkvi = { } }, { 0, 0, 0, 0, 0 }
+
+ local omit = {
+ "char%-def%.lua",
+ "mult%-def%.lua", "mult%-..%.mkii", "mult%-m..%.mkii",
+ }
+ local skip = {
+ "prag%-.*%.tex", "docs%-.*.tex", "list%-.*%.tex", "test%-.*%.tex", "demo%-.*%.tex",
+ "opti%-.*%.tex", "chrt%-.*%.tex", ".*%-old", ".*%-obs", ".*%-tst", "supp%-.*%.tex",
+ "colo%-pan.tex", ".*test.*"
+ }
+ local types = {
+ "tex", "mkii", "mkiv", "mkvi", "lua"
+ }
+ local patterns = {
+ "^([a-z][a-z][a-z][a-z])%-[a-z0-9%-]+%.[a-z]+",
+ "^([xms])%-[a-z0-9%-]+%.[a-z]+",
+ }
+
+ local function collect(list,suffix,n)
+ local path = document.arguments.basepath or file.dirname(resolvers.find_file("context.mkiv"),".")
+ local pattern = path .. "/*." .. suffix .. "$" -- avoid bla.tex~
+ local texfiles = dir.glob(pattern)
+ for i=1,#texfiles do
+ local name = texfiles[i]
+ local base = file.basename(name)
+ for p=1,#patterns do
+ local category = match(base,patterns[p])
+ if category and lfs.isfile(name) then
+ local okay = true
+ for s=1,#skip do
+ if find(base,skip[s]) then
+ okay = false
+ break
+ end
+ end
+ if okay then
+ local lm, sm, cm = list[category], size[category], comp[category]
+ if not lm then
+ lm, sm, cm = { 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0 }
+ list[category], size[category], comp[category] = lm, sm, cm
+ end
+ lm[n] = lm[n] + 1
+ local done = true
+ for o=1,#omit do
+ if find(base,omit[o]) then
+ done = false
+ break
+ end
+ end
+ local data = io.loaddata(name)
+ if data then
+ if suffix == "lua" then
+ data = gsub(data,"%-%-%[%[.-%]%]%-%-","")
+ data = gsub(data,"%-%-.-[\n\r]","")
+ else
+ data = gsub(data,"%%.-[\n\r]","")
+ end
+ data = gsub(data,"%s","")
+ else
+ logs.report("error","unknown file %a",name)
+ data = ""
+ end
+ sm[n+5] = sm[n+5] + #data
+ if done then
+ sm[n] = sm[n] + #data
+ else
+ cm[n] = cm[n] + 1
+ end
+ end
+ end
+ end
+ end
+ end
+
+ local function prepare(what)
+ if next(list) then
+ -- already loaded
+ else
+ for i=1,#types do
+ collect(list,types[i],i)
+ end
+ for category in next, list do
+ pattern ="{"..category.."%-"
+ for suffix, t in next, used do
+ local data = io.loaddata(resolvers.find_file("context."..suffix))
+ if data and find(data,pattern) then
+ t[category] = true
+ end
+ end
+ end
+ end
+ local max, what = 0, (what == "size" and size) or list
+ for k, v in table.sortedpairs(what) do
+ for i=1,5 do if v[i] > max then max = v[i] end end
+ end
+ return max, what, function(n) return (max == 0 and 0) or (n == 0 and 0) or n/max end
+ end
+
+ local f_norm = string.formatters["%0.3f"]
+
+ function document.context_state_1(what)
+ local max, what, norm = prepare(what)
+ context.starttabulate { "|Tl|T|T|T|T|T|" }
+ context.NC()
+ context(category)
+ context.NC()
+ for i=1,#types do
+ local n, m = 0, 0
+ for k, v in next, list do
+ local nn, mm = what[k][i], what[k][i+5]
+ n = n + nn
+ m = m + (mm or nn)
+ end
+ context.Top(types[i],norm(max),n,m)
+ context.NC()
+ end
+ context.NC()
+ context.NR()
+ context.HL()
+ for k, v in table.sortedpairs(what) do
+ local c = what == size and comp[k] or nope
+ context.NC()
+ context("%s~%s~~%s~~%s",
+ (used.mkii[k] and "ii") or "~~",
+ (used.mkiv[k] and "iv") or "~~",
+ (used.mkvi[k] and "vi") or "~~",
+ k
+ )
+ -- context("%s\\enspace %s\\quad %s\\quad %s",
+ -- (used.mkii[k] and "ii") or "\\quad",
+ -- (used.mkiv[k] and "iv") or "\\quad",
+ -- (used.mkvi[k] and "vi") or "\\quad",
+ -- k
+ -- )
+ context.NC()
+ for i=1,#types do
+ context.Bar(types[i],v[i],c[i],f_norm(norm(v[i])))
+ context.NC()
+ end
+ context.NR()
+ end
+ context.stoptabulate()
+ end
+
+ function document.context_state_2(what)
+ local max, what, norm = prepare(what)
+ for k, v in table.sortedpairs(what) do
+ local c = (what == size and comp[k]) or nope
+ context.StartUp(k)
+ for i=1,#types do
+ context.Up(types[i],f_norm(norm(v[i])))
+ end
+ context.StopUp()
+ end
+ end
+
+\stopluacode
+
+\definecolor[bar:tex] [middlegreen]
+\definecolor[bar:mkii][middleblue]
+\definecolor[bar:mkiv][middlered]
+\definecolor[bar:mkvi][middleyellow]
+\definecolor[bar:lua] [middlegray]
+
+\newcount\UpCounter
+
+\starttexdefinition Top #what#fraction#total#bigones
+ \hbox to 5em{\hss#total}%
+ \enspace
+ \hbox {#what\ifnum#total=#bigones\else~#bigones\rlap{~+}\fi\hss}%
+\stoptexdefinition
+
+\starttexdefinition Bar #color#size#nobigones#fraction
+ \ifcase#size\else
+ \hbox to 5em{\hss\ifcase#nobigones\else\llap{-~}\fi#size}%
+ \enspace
+ \blackrule[color=bar:#color,width=#fraction\dimexpr 20em\relax,height=.8\strutht]%
+ \fi
+\stoptexdefinition
+
+\starttexdefinition StartUp #name
+ \def\UpName{#name}%
+ \dontleavehmode\framed[frame=off,align={middle,low},height=18em]\bgroup
+\stoptexdefinition
+
+\starttexdefinition StopUp
+ \par\nointerlineskip\blackrule[height=1pt,width=5em,depth=0pt,color=darkgray]
+ \par\tttf\strut\UpName\par
+ \egroup
+ \ifnum\UpCounter=17
+ \par \UpCounter\zerocount
+ \else
+ \kern1em \advance\UpCounter\plusone
+ \fi
+\stoptexdefinition
+
+\starttexdefinition Up #color#width
+ \scratchdimen#width\dimexpr 16em\relax
+ \ifdim\scratchdimen=\zeropoint
+ \kern1em
+ \else
+ \ifdim\scratchdimen<\onepoint \scratchdimen\onepoint \fi
+ \blackrule[color=bar:#color,height=\scratchdimen,width=1em]%
+ \fi
+\stoptexdefinition
+
+\starttexdefinition Show #title#how#what
+ \startTEXpage[offset=1em,width=fit]
+ \hbox{\tttf\strut\currentdate~-~#title}
+ \par
+ \ctxlua{document.context_state_\number#how("#what")}
+ \stopTEXpage
+\stoptexdefinition
+
+% \continueifinputfile{s-inf-01.mkvi}
+
+\starttext
+ \Show
+ {The number of files used in ConTeXt (base modules and styles).}
+ {1}{number}
+ \Show
+ {The size of (core) files used in ConTeXt (- indicates exclusion of large data files; + indicates inclusion of large data files; comment and spaces removed.)}
+ {1}{size}
+ \Show
+ {The relative number of files used in ConTeXt (tex, mkii, mkiv, mkvi, lua).}
+ {2}{number}
+ \Show
+ {The relative size of files used in ConTeXt (tex, mkii, mkiv, mkvi, lua).}
+ {2}{size}
+\stoptext
diff --git a/tex/context/modules/mkiv/s-inf-02.mkiv b/tex/context/modules/mkiv/s-inf-02.mkiv
new file mode 100644
index 000000000..cf8d572bc
--- /dev/null
+++ b/tex/context/modules/mkiv/s-inf-02.mkiv
@@ -0,0 +1,27 @@
+%D \module
+%D [ file=s-inf-02,
+%D version=2009.11.25,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Information 2 (filenames),
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\starttext
+
+\starttabulate [|T|T|]
+ \NC \type{\jobname} \NC \jobname \NC \NR
+ \NC \type{\jobfilename} \NC \jobfilename \NC \NR
+ \NC \type{\jobfilesuffix} \NC \jobfilesuffix \NC \NR
+ \NC \type{\inputfilename} \NC \inputfilename \NC \NR
+ \NC \type{\inputfilebarename} \NC \inputfilebarename \NC \NR
+ \NC \type{\inputfilesuffix} \NC \inputfilesuffix \NC \NR
+ \NC \type{\outputfilename} \NC \outputfilename \NC \NR
+ \NC \type{\operatingsystem} \NC \operatingsystem \NC \NR
+\stoptabulate
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-inf-03.mkiv b/tex/context/modules/mkiv/s-inf-03.mkiv
new file mode 100644
index 000000000..a253bed77
--- /dev/null
+++ b/tex/context/modules/mkiv/s-inf-03.mkiv
@@ -0,0 +1,372 @@
+% \nopdfcompression
+
+% \starttext
+% There is a loop somewhere ... todo.
+% \stoptext
+
+\enablemode[tablet] % whatever that means
+
+\setupbodyfont[dejavu]
+
+\doifelsemode {tablet} {
+
+ \setuppapersize
+ [S6,landscape]
+ [S6,landscape]
+
+ \definefont
+ [TitlePageFont]
+ [MonoBold at 15pt]
+
+ \setupbodyfont
+ [tt,8pt]
+
+} {
+
+ \definefont
+ [TitlePageFont]
+ [MonoBold at 18pt]
+
+ \setupbodyfont
+ [tt]
+
+}
+
+\setuplayout
+ [header=0cm,
+ footer=1cm,
+ backspace=.5cm,
+ topspace=.5cm,
+ width=middle,
+ height=middle]
+
+\setuphead
+ [title]
+ [style=\ttc,
+ interaction=reference]
+
+\definehead
+ [xtitle]
+ [title]
+
+\setuphead
+ [xtitle]
+ [color=darkgreen]
+
+\setupfootertexts
+ [\llap{\goto{\hbox to 5cm{\hss previous}}[previouspage]}%
+ \quad\pagenumber\quad
+ \rlap{\goto{\hbox to 5cm{next\hss}}[nextpage]}]
+
+\setupinteraction
+ [state=start,
+ style=,
+ color=,
+ title={ConTeXt MkIV},
+ subtitle={Lua modules and functions},
+ author={Hans Hagen - automatically generated},
+ contrastcolor=]
+
+\setupinteractionscreen
+ [option=bookmark]
+
+\placebookmarks
+ [title,xtitle]
+ [force=yes]
+
+\definecolor[darkyellow][r=.5,g=.5,b=0]
+\definecolor[darkgray] [s=.15]
+
+\nonknuthmode
+
+\starttext
+
+\startbuffer
+\startluacode
+local basiclua = libraries.basiclua
+local basictex = libraries.basictex
+local extratex = libraries.extratex
+local extralua = libraries.extralua
+local obsolete = libraries.obsolete
+
+local find = string.find
+local color, gotolocation = context.color, context["goto"]
+
+for k, v in table.sortedpairs(_G) do
+ if obsolete[k] or find(k,"_") or k == "arg" or k == "utf" then
+ --
+ elseif basiclua[k] then
+ gotolocation(function() color( { "darkred" }, k) end, { k } )
+ elseif extralua[k] then
+ gotolocation(function() color( { "darkgreen" }, k) end, { k } )
+ elseif basictex[k] then
+ gotolocation(function() color( { "darkblue" }, k) end, { k } )
+ elseif extratex[k] then
+ gotolocation(function() color( { "darkyellow" }, k) end, { k } )
+ elseif type(v) == "table" then
+ gotolocation(function() color( { "white" }, k) end, { k } )
+ end
+ context(" ")
+end
+\stopluacode
+\stopbuffer
+
+\setupbackgrounds
+ [page]
+ [background=color,
+ backgroundcolor=darkgray,
+ backgroundoffset=2mm]
+
+\startpagemakeup
+ \vfill
+ \pagereference[global]
+ \startnarrower[10mm]
+ \setupalign[nothyhenated,middle,broad]
+ \TitlePageFont \setupinterlinespace
+ \getbuffer % luabuffer
+ \par
+ \stopnarrower
+ \vfill
+ \vfill
+ \hskip10mm\scale[width=\dimexpr\paperwidth-20mm\relax]{\ttbf\white\ConTeXt\ MkIV}
+ \par
+ % \hskip10mm\scale[width=\dimexpr\paperwidth-20mm\relax]{\white \strut Lua infrastructure \emdash\ \currentdate}
+ \vfill
+\stoppagemakeup
+
+\setupbackgrounds
+ [page]
+ [background=]
+
+\startluacode
+local builtin = libraries.builtin
+local globals = libraries.globals
+local basiclua = libraries.basiclua
+local basictex = libraries.basictex
+local extratex = libraries.extratex
+local extralua = libraries.extralua
+local obsolete = libraries.obsolete
+
+local sortedkeys = table.sortedkeys
+local mark = storage.mark
+local marked = storage.marked
+local gsub = string.gsub
+local sub = string.sub
+local byte = string.byte
+local upper = string.upper
+
+local skipglobal = table.tohash {
+ "_G", "_M", "_ENV", "",
+ "context", "modules", "global", "arg", "utf", 1,
+ "_ptbs_", "_pcol_", "_plib_", "_clib_", "_tlib_",
+ "kpse", "commands",
+}
+
+local skipkeys = table.tohash {
+ "_pcol_", "_plib_", "_clib_", "_tlib_", "_bpnf_", "_ptbs_",
+ "_cldf_", "_cldn_", "_cldo_",
+ "_clmb_", "_clme_", "_clmm_", "_clmn_", "_clma_", "_clmh_",
+ "_G", "_M", "_ENV", "",
+ -- "global", "shortcuts",
+ "_VERSION", "_COPYRIGHT", "_DESCRIPTION", "_NAME", "_PACKAGE", "__unload",
+}
+
+local sameglobal = {
+ ["global"] = "_G",
+ -- ["commands"] = "cs", -- already gone
+}
+
+-- -- -- -- -- -- -- -- -- -- -- -- --
+-- this should be done internally
+-- -- -- -- -- -- -- -- -- -- -- -- --
+
+for k,v in next, modules do
+ mark(v)
+end
+
+mark(document.arguments)
+mark(environment.arguments)
+mark(environment.engineflags)
+mark(characters.data)
+
+-- -- -- -- -- -- -- -- -- -- -- -- --
+-- -- -- -- -- -- -- -- -- -- -- -- --
+
+local variant = 1 -- all parents
+local variant = 2 -- parent name too
+local variant = 3 -- no parents
+
+local done = { }
+
+local function childtables(key,tab,handler,depth)
+ depth = depth or 1
+ local keys = sortedkeys(tab) -- no sorted_pairs
+ for i=1,#keys do
+ local k = keys[i]
+-- if k ~= "_G" and k ~= "_M" and type(k) == "string" then
+ if not skipkeys[k] and type(k) == "string" then
+ local v = tab[k]
+ local t = type(v)
+ local s = k
+ if variant ~= 3 then
+ s = key and (key .. "." .. s) or s
+ end
+ if t == "table" then
+ if marked(v) then
+ t = "data"
+ handler(s,t,depth)
+ else
+if done[v] then
+ -- logs.report("inf-03","key %a in %a already done",k,v)
+else
+ done[v] = true
+ handler(s,t,depth)
+ if variant == 3 then
+ childtables(false,v,handler,depth+1)
+ elseif variant == 2 then
+ childtables(k,v,handler,depth+1)
+ else
+ childtables(s,v,handler,depth+1)
+ end
+ end
+end
+ else
+ handler(s,t,depth)
+ end
+ end
+ end
+end
+
+local NC, NR = context.NC, context.NR
+local overstrike, rlap, bf = context.overstrike, context.rlap, context.bf
+local color, gotolocation = context.color, context["goto"]
+
+local function cleanup(s)
+ return "\\char" ..byte(s) .. " "
+end
+
+local function handler(k,t,depth)
+ k = gsub(k,"([~#$%%^&{}\\|])",cleanup)
+ NC() rlap("\\quad\\tx\\kern" .. (depth or 0).. "em" .. upper(sub(t,1,1)) .. " ".. k) NC() NC() NR()
+end
+
+local function show(title,subtitle,alias,builtin,t,lib,libcolor,glo,glocolor,mark,obsolete)
+ -- todo: table as argument
+-- print(title,subtitle,alias,builtin,t,lib,libcolor,glo,glocolor,mark,obsolete)
+ local keys = sortedkeys(t) -- no sorted_pairs
+ if #keys > 0 then
+ local fulltitle = title
+ if subtitle and subtitle ~= "" then
+ fulltitle = fulltitle .. " (" .. subtitle .. ")"
+ end
+ if alias and alias ~= "" then
+ fulltitle = fulltitle .. " (alias: " .. alias .. ")"
+ end
+ if builtin then
+ context.startxtitle { reference = title, title = fulltitle, backreference = "global" }
+ else
+ context.starttitle { reference = title, title = fulltitle, backreference = "global" }
+ end
+ context.startcolumns { n = 2 }
+ context.starttabulate { "|||" }
+ local t_obsolete = t.obsolete
+ if type(t_obsolete) ~= "table" then
+ t_obsolete = nil
+ end
+ for i=1,#keys do
+ local k = keys[i]
+ local v = t[k]
+ if k and k ~= "obsolete" and not skipkeys[k] and (not obsolete or not obsolete[k]) then
+ local inlib = lib and lib[k]
+ local inglo = glo and glo[k]
+ local t = type(v)
+ local kstr, tstr = k, t
+ local obs = t_obsolete and t_obsolete[k]
+ if obs then
+ tstr = function() overstrike(t) end
+ kstr = function() overstrike(k) end
+ end
+ local marked = marked(v)
+ if marked then
+ tstr = "data table"
+ end
+ if t == "table" then
+ local m = getmetatable(v)
+ if m and m.__call then
+ tstr = "function"
+ end
+ end
+ if not mark then
+ --
+ elseif inlib and tostring(inlib) ~= tostring(v) then
+ tstr = "overloaded ".. tstr
+ elseif inglo and tostring(inglo) ~= tostring(v) then
+ tstr = "overloaded ".. tstr
+ end
+ NC() bf()
+ if inlib then
+ if not mark and t == "table" then
+ gotolocation(function() color( { libcolor }, kstr) end, { k } )
+ else
+ color( { libcolor }, kstr)
+ end
+ elseif inglo then
+ if not mark and t == "table" then
+ gotolocation(function() color( { glocolor }, kstr) end, { k } )
+ else
+ color( { glocolor }, kstr)
+ end
+ else
+ if not mark and t == "table" then
+ gotolocation(k, { kstr } )
+ else
+ context(kstr)
+ end
+ end
+ NC()
+ if inlib then
+ color( { libcolor }, tstr)
+ elseif inglo then
+ color( { glocolor }, tstr)
+ else
+ context(tstr)
+ end
+ NC() NR()
+ if mark and t == "table" and title ~= "libraries" and title ~= "package" and not marked then
+ childtables(false,v,handler) -- (k,v,handler)
+ end
+ end
+ end
+ context.stoptabulate()
+ context.stopcolumns()
+ if builtin then
+ context.stopxtitle()
+ else
+ context.stoptitle()
+ end
+ end
+end
+
+show("global","",sameglobal.global,false,_G,builtin,"darkgreen",globals,"darkblue",false,obsolete)
+
+-- inspect(table.sortedkeys(context))
+
+for k, v in table.sortedpairs(_G) do
+ if not skipglobal[k] and not obsolete[k] and type(v) == "table" and not marked(v) then
+
+ -- local mt = getmetatable(v)
+ -- print("!!!!!!!!!!",k,v,mt,mt and mt.__index)
+
+ if basiclua[k] then show(k,"basic lua",sameglobal[k],basiclua[k],v,builtin[k],"darkred", false,false,true)
+ elseif extralua[k] then show(k,"extra lua",sameglobal[k],extralua[k],v,builtin[k],"darkred", false,false,true)
+ elseif basictex[k] then show(k,"basic tex",sameglobal[k],basictex[k],v,builtin[k],"darkred", false,false,true)
+ elseif extratex[k] then show(k,"extra tex",sameglobal[k],extratex[k],v,builtin[k],"darkred", false,false,true)
+ else
+ show(k,"context", sameglobal[k],false, v,builtin[k],"darkyellow",false,false,true)
+ end
+ end
+end
+
+\stopluacode
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-inf-04.mkiv b/tex/context/modules/mkiv/s-inf-04.mkiv
new file mode 100644
index 000000000..862bed72f
--- /dev/null
+++ b/tex/context/modules/mkiv/s-inf-04.mkiv
@@ -0,0 +1,48 @@
+\usemodule[art-01]
+
+\setupbodyfont[10pt]
+
+\starttext
+
+ \startluacode
+
+ context.subject("Configuration files")
+
+ local configurations = resolvers.instance.specification
+
+ context.starttabulate { "|Tl|Tl|" }
+ for i=1,#configurations do
+ context.NC()
+ context(i)
+ context.NC()
+ context.verbatim(resolvers.resolve(configurations[i]))
+ context.NC()
+ context.NR()
+ end
+ context.stoptabulate()
+
+ local list = resolvers.expandedpathfromlist(resolvers.splitpath(resolvers.luacnfspec))
+
+ context.subject("Configuration paths")
+
+ -- context.verbatim(resolvers.luacnfspec)
+
+ context.starttabulate { "|Tl|Tl|" }
+ for i=1,#list do
+ local li = resolvers.resolve(list[i])
+ context.NC()
+ if lfs.isdir(li) then
+ context("-")
+ else
+ context("+")
+ end
+ context.NC()
+ context.verbatim(li)
+ context.NC()
+ context.NR()
+ end
+ context.stoptabulate()
+
+ \stopluacode
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-languages-counters.lua b/tex/context/modules/mkiv/s-languages-counters.lua
new file mode 100644
index 000000000..436e64a64
--- /dev/null
+++ b/tex/context/modules/mkiv/s-languages-counters.lua
@@ -0,0 +1,52 @@
+if not modules then modules = { } end modules ['s-languages-counters'] = {
+ version = 1.001,
+ comment = "companion to s-languages-counters.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.languages = moduledata.languages or { }
+moduledata.languages.counters = moduledata.languages.counters or { }
+
+local data = converters.verbose.data
+
+function moduledata.languages.counters.showverbose(specification)
+ specification = interfaces.checkedspecification(specification)
+ local list = utilities.parsers.settings_to_array(specification.language or "")
+ if #list == 0then
+ return
+ end
+ local used = { }
+ local words = { }
+ for i=1,#list do
+ local ai = list[i]
+ local di = data[ai]
+ if di and di.words then
+ used[#used+1] = ai
+ table.merge(words,di.words)
+ end
+ end
+ context.starttabulate { string.rep("|l",#used) .. "|r|" }
+ context.HL()
+ context.NC()
+ for i=1,#used do
+ context.bold(used[i])
+ context.NC()
+ end
+ context.bold("number")
+ context.NC()
+ context.NR()
+ context.HL()
+ for k, v in table.sortedhash(words) do
+ context.NC()
+ for i=1,#used do
+ context(data[used[i]].words[k] or "")
+ context.NC()
+ end
+ context(k)
+ context.NC()
+ context.NR()
+ end
+ context.stoptabulate()
+end
diff --git a/tex/context/modules/mkiv/s-languages-counters.mkiv b/tex/context/modules/mkiv/s-languages-counters.mkiv
new file mode 100644
index 000000000..fa938d65a
--- /dev/null
+++ b/tex/context/modules/mkiv/s-languages-counters.mkiv
@@ -0,0 +1,30 @@
+%D \module
+%D [ file=s-languages-counters, % s-lan-05,
+%D version=2011.05.01, % older
+%D title=\CONTEXT\ Style File,
+%D subtitle=Language Counters,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[languages-counters]
+
+\registerctxluafile{s-languages-counters}{}
+
+\installmodulecommandluasingle \showverbosecounters {moduledata.languages.counters.showverbose}
+
+\stopmodule
+
+\continueifinputfile{s-languages-counters.mkiv}
+
+\usemodule[art-01]
+
+\starttext
+
+ \showverbosecounters[language={en,es}]
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-languages-frequencies.lua b/tex/context/modules/mkiv/s-languages-frequencies.lua
new file mode 100644
index 000000000..16213a412
--- /dev/null
+++ b/tex/context/modules/mkiv/s-languages-frequencies.lua
@@ -0,0 +1,33 @@
+if not modules then modules = { } end modules ['s-languages-frequencies'] = {
+ version = 1.001,
+ comment = "companion to s-languages-frequencies.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.languages = moduledata.languages or { }
+moduledata.languages.frequencies = moduledata.languages.frequencies or { }
+
+function moduledata.languages.frequencies.showlist(specification)
+ specification = interfaces.checkedspecification(specification)
+ local t = languages.frequencies.getdata(specification.language or languages.current())
+ context.starttabulate { "|lT|cw(2em)|r|" }
+ context.NC()
+ context.formatted.rlap("%s: %p",t.language,languages.frequencies.averagecharwidth(t.language))
+ context.NC()
+ context.NC()
+ context.NR()
+ context.HL()
+ for k, v in table.sortedhash(t.frequencies) do
+ context.NC()
+ context("%U",k)
+ context.NC()
+ context("%c",k)
+ context.NC()
+ context("%0.3f",v)
+ context.NC()
+ context.NR()
+ end
+ context.stoptabulate()
+end
diff --git a/tex/context/modules/mkiv/s-languages-frequencies.mkiv b/tex/context/modules/mkiv/s-languages-frequencies.mkiv
new file mode 100644
index 000000000..01a1f5682
--- /dev/null
+++ b/tex/context/modules/mkiv/s-languages-frequencies.mkiv
@@ -0,0 +1,38 @@
+%D \module
+%D [ file=s-languages-frequencies, % s-lan-06,
+%D version=2013.03.22,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Language Frequencies,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[languages-frequencies]
+
+\registerctxluafile{s-languages-frequencies}{}
+
+\installmodulecommandluasingle \showfrequencies {moduledata.languages.frequencies.showlist}
+
+\stopmodule
+
+\continueifinputfile{s-languages-frequencies.mkiv}
+
+\usemodule[art-01]
+
+\starttext
+
+ \hsize65\averagecharwidth \the\hsize: \input ward \par
+
+ \showfrequencies \page
+
+ \mainlanguage[de]
+
+ \hsize65\averagecharwidth \the\hsize: \input ward \par
+
+ \showfrequencies \page
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-languages-hyphenation.lua b/tex/context/modules/mkiv/s-languages-hyphenation.lua
new file mode 100644
index 000000000..c5a4f91f1
--- /dev/null
+++ b/tex/context/modules/mkiv/s-languages-hyphenation.lua
@@ -0,0 +1,135 @@
+if not modules then modules = { } end modules ['s-languages-hyphenation'] = {
+ version = 1.001,
+ comment = "companion to s-languages-hyphenation.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.languages = moduledata.languages or { }
+moduledata.languages.hyphenation = moduledata.languages.hyphenation or { }
+
+local a_colormodel = attributes.private('colormodel')
+
+local nodecodes = nodes.nodecodes
+local nodepool = nodes.pool
+local disc_code = nodecodes.disc
+local glyph_code = nodecodes.glyph
+local emwidths = fonts.hashes.emwidths
+local exheights = fonts.hashes.exheights
+local newkern = nodepool.kern
+local newrule = nodepool.rule
+local newglue = nodepool.glue
+
+local insert_node_after = node.insert_after
+local traverse_by_id = node.traverse_id
+local hyphenate = languages.hyphenators.handler -- lang.hyphenate
+local find_tail = node.tail
+local remove_node = nodes.remove
+
+local tracers = nodes.tracers
+local colortracers = tracers and tracers.colors
+local setnodecolor = colortracers.set
+
+local function identify(head,marked)
+ local current, prev = head, nil
+ while current do
+ local id = current.id
+ local next = current.next
+ if id == disc_code then
+ if prev and next then -- and next.id == glyph_code then -- catch other usage of disc
+ marked[#marked+1] = prev
+ end
+ elseif id == glyph_code then
+ prev = current
+ end
+ current = next
+ end
+end
+
+local function strip(head,marked)
+ for i=1,#marked do
+ local prev = marked[i]
+ remove_node(head,prev.next,true)
+ end
+end
+
+local function mark(head,marked,w,h,d,how)
+ for i=1,#marked do
+ local prev = marked[i]
+ local font = prev.font
+ local em = emwidths[font]
+ local ex = exheights[font]
+ local width = w*em
+ local rule = newrule(width,h*ex,d*ex)
+ head, prev = insert_node_after(head,prev,newkern(-width/2))
+ head, prev = insert_node_after(head,prev,rule)
+ head, prev = insert_node_after(head,prev,newkern(-width/2))
+ head, prev = insert_node_after(head,prev,newglue(0))
+ setnodecolor(rule,how,prev[a_colormodel])
+ end
+end
+
+local langs, tags, noflanguages = { }, { }, 0
+
+local colorbytag = false
+
+function moduledata.languages.hyphenation.showhyphens(head)
+ if noflanguages > 0 then
+ local marked = { }
+ for i=1,noflanguages do
+ local m = { }
+ local l = langs[i]
+ marked[i] = m
+ for n in traverse_by_id(glyph_code,head) do
+ n.lang = l
+ end
+ languages.hyphenators.methods.original(head)
+ identify(head,m)
+ strip(head,m)
+ end
+ for i=noflanguages,1,-1 do
+ local l = noflanguages - i + 1
+ mark(head,marked[i],1/16,l/2,l/4,"hyphenation:"..(colorbytag and tags[i] or i))
+ end
+ return head, true
+ else
+ return head, false
+ end
+end
+
+local savedlanguage
+
+function moduledata.languages.hyphenation.startcomparepatterns(list)
+ if list and list ~= "" then
+ tags = utilities.parsers.settings_to_array(list)
+ end
+ savedlanguage = tex.language
+ tex.language = 0
+ noflanguages = #tags
+ for i=1,noflanguages do
+ langs[i] = tags[i] and languages.getnumber(tags[i])
+ end
+ nodes.tasks.enableaction("processors","moduledata.languages.hyphenation.showhyphens")
+end
+
+function moduledata.languages.hyphenation.stopcomparepatterns()
+ noflanguages = 0
+ tex.language = savedlanguage or tex.language
+ nodes.tasks.disableaction("processors","moduledata.languages.hyphenation.showhyphens")
+end
+
+function moduledata.languages.hyphenation.showcomparelegend(list)
+ if list and list ~= "" then
+ tags = utilities.parsers.settings_to_array(list)
+ end
+ for i=1,#tags do
+ if i > 1 then
+ context.enspace()
+ end
+ context.color( { "hyphenation:"..(colorbytag and tags[i] or i) }, tags[i])
+ end
+end
+
+nodes.tasks.appendaction("processors","before","moduledata.languages.hyphenation.showhyphens")
+nodes.tasks.disableaction("processors","moduledata.languages.hyphenation.showhyphens")
diff --git a/tex/context/modules/mkiv/s-languages-hyphenation.mkiv b/tex/context/modules/mkiv/s-languages-hyphenation.mkiv
new file mode 100644
index 000000000..6662dbf2f
--- /dev/null
+++ b/tex/context/modules/mkiv/s-languages-hyphenation.mkiv
@@ -0,0 +1,77 @@
+%D \module
+%D [ file=s-languages-hyphenation, % s-lan-04,
+%D version=2011.05.01,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Language Hyphenation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D For Mojca.
+
+\startmodule[languages-hyphenation]
+
+\registerctxluafile{s-languages-hyphenation}{}
+
+\unprotect
+
+\unexpanded\def\startcomparepatterns
+ {\dosingleempty\module_languages_hyphenation_start}
+
+\unexpanded\def\module_languages_hyphenation_start[#1]%
+ {\begingroup
+ \par
+ % \language\zerocount
+ % \setupalign[\v!nothyphenated]%
+ \ctxlua{moduledata.languages.hyphenation.startcomparepatterns("#1")}}
+
+\unexpanded\def\stopcomparepatterns
+ {\par
+ \ctxlua{moduledata.languages.hyphenation.stopcomparepatterns()}%
+ \endgroup}
+
+\installmodulecommandluasingle \showcomparepatternslegend {moduledata.languages.hyphenation.showcomparelegend}
+
+\protect
+
+\definecolor[hyphenation:1] [r=.8]
+\definecolor[hyphenation:2] [g=.8]
+\definecolor[hyphenation:3] [b=.8]
+\definecolor[hyphenation:4] [r=.4,g=.4]
+
+\definecolor[hyphenation:en] [hyphenation:1]
+\definecolor[hyphenation:de] [hyphenation:2]
+\definecolor[hyphenation:nl] [hyphenation:3]
+\definecolor[hyphenation:fr] [hyphenation:4]
+
+\stopmodule
+
+\continueifinputfile{s-languages-hyphenation.mkiv}
+
+\usemodule[art-01]
+
+\starttext
+
+\def|#1|{-}
+
+ \startsubject{Normal text}
+ \input tufte
+ \stopsubject
+
+ \startsubject{Compare hyphenation points of \showcomparepatternslegend[en,de]}
+ \startcomparepatterns
+ \input tufte \quad (\showcomparepatternslegend)
+ \stopcomparepatterns
+ \stopsubject
+
+ \startsubject{Compare hyphenation points}
+ \startcomparepatterns[de,nl,en,fr]
+ \input tufte \quad (\showcomparepatternslegend)
+ \stopcomparepatterns
+ \stopsubject
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-languages-sorting.lua b/tex/context/modules/mkiv/s-languages-sorting.lua
new file mode 100644
index 000000000..82a0827bb
--- /dev/null
+++ b/tex/context/modules/mkiv/s-languages-sorting.lua
@@ -0,0 +1,118 @@
+if not modules then modules = { } end modules ['s-languages-system'] = {
+ version = 1.001,
+ comment = "companion to s-languages-system.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.languages = moduledata.languages or { }
+moduledata.languages.sorting = moduledata.languages.sorting or { }
+
+local formatters = string.formatters
+local utfbyte, utfcharacters = utf.byte, utf.characters
+local sortedpairs = table.sortedpairs
+
+local definitions = sorters.definitions
+local constants = sorters.constants
+local replacementoffset = constants.replacementoffset
+
+local currentfont = font.current
+local fontchars = fonts.hashes.characters
+
+local c_darkblue = { "darkblue" }
+local c_darkred = { "darkred" }
+local f_chr = formatters["\\tttf%H"]
+
+local function chr(str,done)
+ if done then
+ context.space()
+ end
+ local c = fontchars[currentfont()]
+ for s in utfcharacters(str) do
+ local u = utfbyte(s)
+ if c[u] then
+ context(s)
+ elseif u > replacementoffset then
+ context.color(c_darkblue, f_chr(u))
+ else
+ context.color(c_darkred, f_chr(u))
+ end
+ end
+ return true
+end
+
+local function map(a,b,done)
+ if done then
+ context.space()
+ end
+ -- context.tttf()
+ chr(a)
+ context("=")
+ chr(b)
+ return true
+end
+
+local function nop()
+ -- context.tttf()
+ context("none")
+end
+
+local function key(data,field)
+ context.NC()
+ context(field)
+ context.NC()
+ context(data[field])
+ context.NC()
+ context.NR()
+end
+
+function moduledata.languages.sorting.showinstalled(tag)
+ if not tag or tag == "" or tag == interfaces.variables.all then
+ for tag, data in sortedpairs(definitions) do
+ moduledata.languages.sorting.showinstalled (tag)
+ end
+ else
+ sorters.update() -- syncs data
+ local data = definitions[tag]
+ if data then
+ context.starttabulate { "|lB|pl|" }
+ key(data,"language")
+ key(data,"parent")
+ key(data,"method")
+ context.NC()
+ context("replacements")
+ context.NC()
+ local replacements = data.replacements
+ if #replacements == 0 then
+ nop()
+ else
+ for i=1,#replacements do
+ local r = replacements[i]
+ map(r[1],r[2],i > 1)
+ end
+ end
+ context.NC()
+ context.NR()
+ context.NC()
+ context("order")
+ context.NC()
+ local orders = data.orders
+ for i=1,#orders do
+ chr(orders[i],i > 1)
+ end
+ context.NC()
+ context.NR()
+ context.NC()
+ context("entries")
+ context.NC()
+ local done = false
+ for k, e in sortedpairs(data.entries) do
+ done = map(k,e,done)
+ end
+ context.NC()
+ context.NR()
+ context.stoptabulate()
+ end
+ end
+end
diff --git a/tex/context/modules/mkiv/s-languages-sorting.mkiv b/tex/context/modules/mkiv/s-languages-sorting.mkiv
new file mode 100644
index 000000000..67acda6f9
--- /dev/null
+++ b/tex/context/modules/mkiv/s-languages-sorting.mkiv
@@ -0,0 +1,30 @@
+%D \module
+%D [ file=s-languages-sorting, % s-lan-02.mkiv
+%D version=2010.09.21,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Language Sorting,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[languages-sorting]
+
+\registerctxluafile{s-languages-sorting}{}
+
+\installmodulecommandluasingle \showinstalledsorting {moduledata.languages.sorting.showinstalled}
+
+\stopmodule
+
+\continueifinputfile{s-languages-sorting.mkiv}
+
+\usemodule[art-01]
+
+\starttext
+
+ \showinstalledsorting
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-languages-system.lua b/tex/context/modules/mkiv/s-languages-system.lua
new file mode 100644
index 000000000..3b422db9f
--- /dev/null
+++ b/tex/context/modules/mkiv/s-languages-system.lua
@@ -0,0 +1,62 @@
+if not modules then modules = { } end modules ['s-languages-system'] = {
+ version = 1.001,
+ comment = "companion to s-languages-system.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.languages = moduledata.languages or { }
+moduledata.languages.system = moduledata.languages.system or { }
+
+local NC, NR, HL = context.NC, context.NR, context.HL
+local sortedhash = table.sortedhash
+local registered = languages.registered
+local context = context
+local ctx_NC = context.NC
+local ctx_NR = context.NR
+local ctx_bold = context.bold
+
+function moduledata.languages.system.loadinstalled()
+ context.start()
+ for k, v in table.sortedhash(registered) do
+ context.language{ k }
+ end
+ context.stop()
+end
+
+function moduledata.languages.system.showinstalled()
+ --
+ context.starttabulate { "|l|r|l|l|p(7em)|r|p|" }
+ context.FL()
+ ctx_NC() ctx_bold("tag")
+ ctx_NC() ctx_bold("n")
+ ctx_NC() ctx_bold("parent")
+ ctx_NC() ctx_bold("file")
+ ctx_NC() ctx_bold("synonyms")
+ ctx_NC() ctx_bold("patterns")
+ ctx_NC() ctx_bold("characters")
+ ctx_NC() ctx_NR()
+ context.FL()
+ for k, v in sortedhash(registered) do
+ local parent = v.parent
+ local resources = v.resources
+ local patterns = resources and resources.patterns
+ ctx_NC() context(k)
+ ctx_NC() context(v.number)
+ ctx_NC() context(v.parent)
+ ctx_NC() context(v.patterns)
+ ctx_NC() for k, v in sortedhash(v.synonyms) do context("%s\\par",k) end
+ if patterns then
+ ctx_NC() context(patterns.n)
+ ctx_NC() context("% t",utf.split(patterns.characters))
+ else
+ ctx_NC()
+ ctx_NC()
+ end
+ ctx_NC() ctx_NR()
+ end
+ context.LL()
+ context.stoptabulate()
+ --
+end
diff --git a/tex/context/modules/mkiv/s-languages-system.mkiv b/tex/context/modules/mkiv/s-languages-system.mkiv
new file mode 100644
index 000000000..22991f264
--- /dev/null
+++ b/tex/context/modules/mkiv/s-languages-system.mkiv
@@ -0,0 +1,32 @@
+%D \module
+%D [ file=s-languages-system, % moved from local s-lan-01
+%D version=2013.05.19,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Installed Languages,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[languages-system]
+
+\registerctxluafile{s-languages-system}{}
+
+\installmodulecommandluasingle \showinstalledlanguages {moduledata.languages.system.showinstalled}
+\installmodulecommandluasingle \loadinstalledlanguages {moduledata.languages.system.loadinstalled}
+
+\stopmodule
+
+\continueifinputfile{s-languages-system.mkiv}
+
+\usemodule[art-01]
+
+\starttext
+
+ \loadinstalledlanguages
+ \showinstalledlanguages
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-languages-words.lua b/tex/context/modules/mkiv/s-languages-words.lua
new file mode 100644
index 000000000..ea7aee87b
--- /dev/null
+++ b/tex/context/modules/mkiv/s-languages-words.lua
@@ -0,0 +1,32 @@
+if not modules then modules = { } end modules ['s-languages-words'] = {
+ version = 1.001,
+ comment = "companion to s-languages-words.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.languages = moduledata.languages or { }
+moduledata.languages.words = moduledata.languages.words or { }
+
+function moduledata.languages.words.showwords(specification)
+ local filename = specification.filename or file.addsuffix(tex.jobname,"words")
+ if lfs.isfile(filename) then
+ local w = dofile(filename)
+ if w then
+ -- table.print(w)
+ for cname, category in table.sortedpairs(w.categories) do
+ for lname, language in table.sortedpairs(category.languages) do
+ context.bold(string.format("category: %s, language: %s, total: %s, unique: %s:",
+ cname, lname, language.total or 0, language.unique or 0)
+ )
+ for word, n in table.sortedpairs(language.list) do
+ context(" %s (%s)",word,n)
+ end
+ context.par()
+ end
+ end
+ end
+ end
+end
+
diff --git a/tex/context/modules/mkiv/s-languages-words.mkiv b/tex/context/modules/mkiv/s-languages-words.mkiv
new file mode 100644
index 000000000..4e350bf34
--- /dev/null
+++ b/tex/context/modules/mkiv/s-languages-words.mkiv
@@ -0,0 +1,22 @@
+%D \module
+%D [ file=s-languages-words,
+%D version=2010.10.21,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Language Environment 3,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is used in the test suite.
+
+\startmodule[languages-words]
+
+\registerctxluafile{s-languages-words}{}
+
+\installmodulecommandluasingle \showwords {moduledata.languages.words.showwords}
+
+\stopmodule
diff --git a/tex/context/modules/mkiv/s-map-10.mkiv b/tex/context/modules/mkiv/s-map-10.mkiv
new file mode 100644
index 000000000..c7541babc
--- /dev/null
+++ b/tex/context/modules/mkiv/s-map-10.mkiv
@@ -0,0 +1,494 @@
+%D \module
+%D [file=s-map-10.mkiv,
+%D version=2012.06.06,
+%D title=\CONTEXT\ Style File,
+%D subtitle=\MAPS\ journal style,
+%D author={Hans Hagen, Taco Hoekwater and Siep Kroonenberg},
+%D date=\currentdate,
+%D copyright=NTG/MAPS]
+
+% This module implements the MAPS style for use with the Context
+% macro package. The original MAPS layout was designed and
+% implemented in LaTeX by Taco Hoekwater and Siep Kroonenberg.
+
+% - three layouts:
+% 1. two columns
+% 2. one column, with wide outer margins (option onecolumn)
+% 3. one column, with wide left margin (option asym)
+% - font sizes deviate from TeX's usual geometric progression
+% - use of sans-serif for headers and various details
+% - option realfonts uses Linux Libertine, Euler Math and Inconsolata.
+% This is used for final typesetting.
+% The default font setup, intended for authors, uses Computer
+% Modern Math instead of Euler Math (which is still in beta),
+% and LM Mono instead of Inconsolata.
+
+% A mode nosubsub defines only two levels of sectioning. If you
+% don't need more and use the two-column layout, then this option
+% will probably improve the looks of your paper.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\newif\ifMapsInColumns
+\doifmode{asym}{\enablemode[onecolumn]} % implies onecolumn
+\doifnotmode{onecolumn}{\MapsInColumnstrue}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% fonts
+
+%%%%%%%%% first, font sizes
+\definebodyfontenvironment [7pt][% LaTeX: scriptsize
+ interlinespace=8pt,
+ big=8pt,
+ small=6pt%
+]
+
+\definebodyfontenvironment [8pt][% LaTeX: footnotesize
+ interlinespace=9pt,
+ big=9pt,
+ small=7pt,
+ x=6pt%
+]
+
+\definebodyfontenvironment [9pt][% LaTeX: small
+ interlinespace=10pt,
+ big=10pt,
+ small=8pt,%
+ x=7pt,%
+ script=6pt%
+]
+
+\definebodyfontenvironment [10pt][% LaTeX: normalsize
+ interlinespace=11pt,
+ big=11pt,
+ a=11pt,
+ small=9pt,%
+ x=8pt,%
+ script=7pt%
+]
+
+\definebodyfontenvironment [11pt][% LaTeX: large
+ interlinespace=11pt,
+ big=11pt,
+ small=10pt,%
+ x=9pt,%
+ script=8pt%
+]
+
+\definebodyfontenvironment [14pt][%
+ interlinespace=14pt,
+ big=18pt,
+ small=11pt,
+ x=10pt%
+]
+
+\definebodyfontenvironment [18pt][%
+ interlinespace=18pt,
+ big=24pt,
+ small=14pt,
+ x=10pt%
+]
+
+\definebodyfontenvironment [24pt][%
+ interlinespace=24pt,
+ big=24pt,
+ small=18pt,
+ x=11pt%
+]
+
+%%% font families
+
+\starttypescript [maps]
+\definetypeface [maps] [rm] [serif] [modern] [default] [rscale=0.95]
+\definetypeface [maps] [mm] [math] [modern] [latin-modern]
+\definetypeface [maps] [tt] [mono] [modern] [default] [rscale=0.90]
+\definetypeface [maps] [ss] [sans] [modern] [default] [rscale=0.95]
+\stoptypescript
+
+\startmode[realfonts]
+\usetypescriptfile[type-libertine]
+
+\usetypescriptfile[type-inconsolata]
+
+\starttypescript [maps]
+\definetypeface [maps] [rm] [serif] [libertine] [default]
+\definetypeface [maps] [mm] [math] [euler] [default] [rscale=0.9]
+\definetypeface [maps] [tt] [mono] [inconsolata] [default] [rscale=0.92]
+\definetypeface [maps] [ss] [sans] [modern] [default] [rscale=0.95]
+\stoptypescript
+\stopmode
+
+\setupbodyfont[maps,10pt,rm]
+
+% activate protruding
+\setupinterlinespace[line=11pt]
+
+\setupfontsynonym[handling=pure]
+
+\setupalign[hanging]
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% additional general typographic details
+
+\setupindenting [yes,next,11pt] % indenting after enumerations etc.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% 3 versions of layout with matching headers
+
+\definepapersize
+ [maps]
+ [width=21cm,height=26.5cm]
+
+\setuppapersize
+ [maps][maps]
+
+\setuplayout[
+ [topspace=40pt,
+ height=688pt,
+ header=33pt,
+ margin=106pt,
+ leftmargindistance=11pt,
+ rightmargindistance=11pt]
+
+\setupblank[5.5pt]
+
+\setuppagenumbering [location=]
+
+\definetyping [widetyping]
+
+\setupheader [style=\ss]
+\setupfooter [style=\ss]
+
+\def\AuHead{\MapsRunningAuthor}
+\def\TiHead{\ifnum\pageno=\MapsPage \relax \MapsRunningAuthor \else \MapsRunningTitle\fi}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% sectioning
+
+\setupheads[sectionnumber=no, align=right]
+
+\def\hfonti{\ssbfa}
+\def\hfontii{\ssbf}
+\def\hfontiii{\rm\it}
+
+\doifelsemode{nosubsub}{%
+\setuphead [section][%
+ style=\hfontii,
+ before={\blank[line]},
+ after={}%
+]
+\setuphead [subsection][%
+ style=\hfontiii,
+ alternative=text,
+ distance=6pt,
+ before={\blank[halfline]}%
+]}{%
+\setuphead [section][%
+ style=\hfonti,
+ before={\blank[line]},
+ after={\blank[halfline]}%
+]
+\setuphead [subsection][%
+ style=\hfontii,
+ before={\blank[halfline]},
+ after={}%
+]
+\setuphead [subsubsection][%
+ style=\hfontiii,
+ distance=6pt,
+ alternative=text,
+ before={\blank[halfline]}%
+]}
+
+\doifelsemode{nosubsub}{%
+\setuphead [subject][%
+ style=\hfontii,
+ before={\blank[halfline]},
+ after={}%
+]
+\setuphead [subsubject][%
+ style=\hfontiii,
+ alternative=text,
+ before={\blank[halfline]}%
+]}{%
+\setuphead [subject][%
+ style=\hfonti,
+ before={\blank},
+ after={\blank[halfline]}%
+]
+\setuphead [subsubject][%
+ style=\hfontii,
+ before={\blank[halfline]},
+ after={}%
+]
+\setuphead [subsubsubject][%
+ style=\hfontiii,
+ alternative=text,
+ before={\blank[halfline]}%
+]}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% floats
+
+\setupfloats [location=center, before={\ss}]
+\setupcaptions [headstyle={\ssbf},style={\ssx},
+ suffix=.,distance=6pt,
+ inbetween={\blank[halfline]}]
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% various document elements
+
+\definesymbol [1][\getnamedglyphdirect{file:stmary10}{boxempty}]
+
+\setupitemize[1][packed]
+
+\setupitemize [each][%
+ indentnext=no,
+ align=right,
+ width=1em,
+ distance=0pt%
+]
+
+% an outer form of itemize that does not indent
+% the paragraph.
+
+\definecomplexorsimpleempty\startouteritemize
+\def\complexstartouteritemize[#1]{\begingroup
+ \startitemize[width=1sp,#1]
+ \let\doitem\item
+ \def\item{\doitem[]\hbox{}\kern12pt\rightskip=0pt}%
+}
+
+\def\stopouteritemize{\stopitemize\endgroup}
+
+
+\setupenumerations [indentnext=no]
+
+\setupdescriptions [indentnext=no]
+
+\unexpanded\def\smalltyping{%
+ \switchtobodyfont[tt]%
+ \parindent=0pt
+}
+
+% typing:
+% - prettyverbatim is NOT the default
+% - smaller size
+
+\unexpanded\def
+ \XeTeX{X\lower.5ex\hbox{\kern-.1em\mirror{E}}\kern-.1667em\TeX}
+
+\setuptyping [%
+ style={\smalltyping},
+ option=none,
+ indentnext=no%
+]
+
+\def\footnum#1{#1.}
+
+\setupnotation
+ [footnote]
+ [alternative=serried,
+ before=,
+ after=,
+ location=none,
+ width=\textwidth,
+ before={\blank},
+ numbercommand=,
+ command=\footnum,
+ distance=0.5em]
+
+\setuptabulate
+ [before=\blank,
+ inner=\ss,
+ after=\blank]
+
+\def\startIntroEntry#1%
+ {\startlinecorrection
+ \bgroup
+ \setupalign[right]
+ \setuptolerance[verytolerant]
+ \setupindenting[no]
+ \noindent
+ \switchtobodyfont[9pt]%
+ \setuplocalinterlinespace[line=10pt]%
+ %\hyphenpenalty10000
+ \parfillskip 0pt plus 1fill
+ \rightskip6pt plus 1fill
+ \ss
+ \bgroup\bf #1\par\egroup
+ \ignorespaces }
+
+\def\stopIntroEntry
+ {\par\egroup \stoplinecorrection
+ \blank[line] }
+
+\def\defineIntroEntry[#1][#2][#3]%
+ {\setvalue{start#1}{\startIntroEntry{#2}}%
+ \setvalue {stop#1}{\stopIntroEntry#3}}
+
+\defineIntroEntry[Keywords][Keywords][]
+\defineIntroEntry[Abstract][Abstract][]
+
+% article parameters (other fields and defaults)
+\def\MapsBibData[#1]%
+ {\getparameters [Maps]
+ [SubTitle=,
+ RunningAuthor=,
+ RunningTitle=,
+ Email=,
+ Address=,
+ Page=1,
+ Title=,
+ Author=,
+ Period=,
+ Number=,
+ Year=,
+ #1]%
+ \doifnothing{\MapsPeriod}{%
+ \ifnum \normalmonth<6 \gdef\MapsPeriod{VOORJAAR}\else \gdef\MapsPeriod{NAJAAR}\fi}
+ \doifelseinstring{oorjaar}{\MapsPeriod}{\gdef\MapsPeriod{VOORJAAR}}{}%
+ \doifelseinstring{pring}{\MapsPeriod}{\gdef\MapsPeriod{VOORJAAR}}{}%
+ \doifelseinstring{ajaar}{\MapsPeriod}{\gdef\MapsPeriod{NAJAAR}}{}%
+ \doifelseinstring{utumn}{\MapsPeriod}{\gdef\MapsPeriod{NAJAAR}}{}%
+ \doifnothing{\MapsYear}{\gdef\MapsYear{\the\year}}%
+ \doifnothing{\MapsNumber}{%
+ \ifnum \normalmonth<6
+ \xdef\MapsNumber{\the\numexpr (\the\year-1990)*2\relax}%
+ \else
+ \xdef\MapsNumber{\the\numexpr (\the\year-1990)*2+1\relax}%
+ \fi }%
+ \doifnothing\MapsRunningAuthor
+ {\global\let\MapsRunningAuthor\MapsAuthor}%
+ \doifnothing\MapsRunningTitle
+ {\global\let\MapsRunningTitle\MapsTitle}}%
+
+\def\dostartArticle[#1]{%
+ \MapsBibData[#1]
+ \pageno=\MapsPage
+ \setupuserpagenumber[start=\MapsPage]
+ \startbaselinecorrection
+ \bgroup
+ \hsize = 457pt
+ \let\\\crlf
+ \blank[35pt,force]
+ \switchtobodyfont[24pt]
+ \setupalign[right]
+ {\noindent\bf\MapsTitle\par}
+ \ifx\MapsSubTitle\empty
+ \blank[30pt]
+ \else
+ \bgroup
+ \blank[12pt]
+ \switchtobodyfont[18pt]\noindent \it
+ \advance \rightskip 0pt plus 2em
+ \MapsSubTitle\par
+ \egroup
+ \blank[30pt]
+ \fi
+ \egroup
+ \setupalign[width]
+ \switchtobodyfont[rm,10pt]
+ \stopbaselinecorrection
+ \ifMapsInColumns
+ \startcolumns\hyphenpenalty1000
+ \else
+ \clubpenalty10000
+ \widowpenalty10000
+ \fi
+}
+
+\def\startArticle{\dosingleempty\dostartArticle}
+
+\def\signArticle{%
+ \blank\let\\\crlf
+ \noindent\switchtobodyfont[ss,9pt]%
+ \MapsAuthor
+ \doifsomething{\MapsAddress}{\\\MapsAddress}%
+ \doifsomething{\MapsEmail}{\\\MapsEmail}%
+ \switchtobodyfont[10pt]%
+ \def\signArticle{}%
+}
+
+\def\stopArticle{%
+ \par\signArticle
+ \ifMapsInColumns \stopcolumns \fi
+ \page
+}
+
+\installpagebreakmethod{last}{}
+
+%%% `logos' %%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\unexpanded\def\LaTeX % requested by erik frambach
+ {{\setbox\scratchbox\hbox{L}%
+ \scratchdimen\ht\scratchbox
+ \setbox\scratchbox\hbox{\switchtobodyfont[script]A}%
+ L\kern-.55\wd\scratchbox
+ \raise\scratchdimen\hbox{\lower\ht\scratchbox\copy\scratchbox}%
+ \kern-.2\wd\scratchbox\TeX}}
+
+
+\def\CONTEXT{Con{\TeX}t}
+\def\ConTeXt{Con{\TeX}t}
+\def\METAFONT{Metafont}
+\def\METAPOST{MetaPost}
+\def\POSTSCRIPT{PostScript}
+
+\def\acro#1{{\switchtobodyfont[9pt]#1}}
+
+
+%%%%%%%%%%%
+
+\doifelsemode{onecolumn}{%
+ \setuplayout[width=340pt]
+ \doifelsemode{asym}{% one col, asymmetric
+ \setuplayout[backspace=187.3pt]%
+ \setuptyping [widetyping][oddmargin=-117pt]
+ \setuppagenumbering [alternative={singlesided,doublesided}]
+ \setupheadertexts
+ [{\hbox{}\hskip-117pt\TiHead}]
+ [{\cap{\MapsPeriod\ \MapsYear}\quad\bf \pagenumber\hskip-30pt\hbox{}}]
+ [{\hbox{}\hskip-147pt{\bf \pagenumber}\quad \cap {maps\ \MapsNumber}}]
+ [\AuHead]
+ \setupfootertexts
+ }{% one col, symmetric
+ \setuplayout[backspace=70.3pt]
+ \setuppagenumbering [alternative=doublesided]
+ \setuptyping[blank=halfline]
+ \setupheadertexts
+ [\TiHead]
+ [{\cap{\MapsPeriod\ \MapsYear}\quad\bf \pagenumber\hskip-147pt\hbox{}}]
+ [{\hbox{}\hskip-147pt{\bf \pagenumber}\quad \cap {maps\ \MapsNumber}}]
+ [\AuHead]
+ \setupfootertexts
+}}{% two col
+ \setuplayout[width=457pt]
+ \setupcolumns[n=2,tolerance=verytolerant,distance=11pt]
+ \setuplayout[backspace=70.3pt,grid=yes]
+ \setuppagenumbering [alternative=doublesided]
+ \setuptyping[blank=halfline]
+ \setupheadertexts
+ [\TiHead]
+ [{\cap{\MapsPeriod\ \MapsYear}\quad\bf \pagenumber\hskip-30pt\hbox{}}]
+ [{\hbox{}\hskip-30pt{\bf \pagenumber}\quad \cap {maps\ \MapsNumber}}]
+ [\AuHead]
+ \setupfootertexts
+}
+
+\def\fulltextwidth{457pt}
+
+\def\startdescription
+ {\blank
+ \bgroup
+ \def\sym##1{\par\noindent\hbox{\bf\kern -16pt ##1}\hskip 12pt}
+ \startnarrower[left]
+ }
+\def\stopdescription
+ {\par \stopnarrower \egroup \blank \noindentation }
+
+\frenchspacing
+\setuptolerance[tolerant]
+
+\endinput
diff --git a/tex/context/modules/mkiv/s-math-characters.lua b/tex/context/modules/mkiv/s-math-characters.lua
new file mode 100644
index 000000000..8ff3a8660
--- /dev/null
+++ b/tex/context/modules/mkiv/s-math-characters.lua
@@ -0,0 +1,242 @@
+if not modules then modules = { } end modules['s-math-characters'] = {
+ version = 1.001,
+ comment = "companion to s-math-characters.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is one of the oldest cld files but I'm not going to clean it up.
+
+moduledata.math = moduledata.math or { }
+moduledata.math.characters = moduledata.math.characters or { }
+
+local concat = table.concat
+local lower = string.lower
+local utfchar = utf.char
+local round = math.round
+
+local fontdata = fonts.hashes.identifiers
+local chardata = characters.data
+local blocks = characters.blocks
+
+local no_description = "no description, private to font"
+
+local limited = true
+local fillinthegaps = true
+local upperlimit = 0x0007F
+local upperlimit = 0xF0000
+
+local f_unicode = string.formatters["%U"]
+local f_slot = string.formatters["%s/%0X"]
+
+function moduledata.math.characters.showlist(specification)
+ specification = interfaces.checkedspecification(specification)
+ local id = specification.number -- or specification.id
+ local list = specification.list
+ local showvirtual = specification.virtual == "all"
+ local check = specification.check == "yes"
+ if not id then
+ id = font.current()
+ end
+ if list == "" then
+ list = nil
+ end
+ local tfmdata = fontdata[id]
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local resources = tfmdata.resources
+ local lookuptypes = resources.lookuptypes
+ local virtual = tfmdata.properties.virtualized
+ local names = { }
+ local gaps = mathematics.gaps
+ local sorted = { }
+ if type(list) == "string" then
+ sorted = utilities.parsers.settings_to_array(list)
+ elseif type(list) == "table" then
+ sorted = list
+ elseif fillinthegaps then
+ sorted = table.keys(characters)
+ for k, v in next, gaps do
+ if characters[v] then
+ sorted[#sorted+1] = k
+ end
+ end
+ table.sort(sorted)
+ else
+ sorted = table.sortedkeys(characters)
+ end
+ if virtual then
+ for k, v in ipairs(tfmdata.fonts) do
+ local id = v.id
+ local name = fontdata[id].properties.name
+ names[k] = (name and file.basename(name)) or id
+ end
+ end
+ if check then
+ for k, v in table.sortedhash(blocks) do
+ if v.math then
+ local first = v.first
+ local last = v.last
+ local f, l = 0, 0
+ if first and last then
+ for unicode=first,last do
+ local code = gaps[unicode] or unicode
+ local char = characters[code]
+ if char and not (char.commands and not showvirtual) then
+ f = unicode
+ break
+ end
+ end
+ for unicode=last,first,-1 do
+ local code = gaps[unicode] or unicode
+ local char = characters[code]
+ if char and not (char.commands and not showvirtual) then
+ l = unicode
+ break
+ end
+ end
+ context.showmathcharacterssetrange(k,f,l)
+ end
+ end
+ end
+ else
+ context.showmathcharactersstart()
+ for _, unicode in next, sorted do
+ if not limited or unicode < upperlimit then
+ local code = gaps[unicode] or unicode
+ local char = characters[code]
+ local desc = descriptions[code]
+ local info = chardata[code]
+ if char then
+ local commands = char.commands
+ if commands and not showvirtual then
+ -- skip
+ else
+ local next_sizes = char.next
+ local v_variants = char.vert_variants
+ local h_variants = char.horiz_variants
+ local slookups = desc and desc.slookups
+ local mlookups = desc and desc.mlookups
+ local mathclass = info.mathclass
+ local mathspec = info.mathspec
+ local mathsymbol = info.mathsymbol
+ local description = info.description or no_description
+ context.showmathcharactersstartentry()
+ context.showmathcharactersreference(f_unicode(unicode))
+ context.showmathcharactersentryhexdectit(f_unicode(code),code,lower(description))
+ context.showmathcharactersentrywdhtdpic(round(char.width or 0),round(char.height or 0),round(char.depth or 0),round(char.italic or 0))
+ if virtual and commands then
+ local t = { }
+ for i=1,#commands do
+ local ci = commands[i]
+ if ci[1] == "slot" then
+ local fnt, idx = ci[2], ci[3]
+ t[#t+1] = f_slot(names[fnt] or fnt,idx)
+ end
+ end
+ if #t > 0 then
+ context.showmathcharactersentryresource(concat(t,", "))
+ end
+ end
+ if mathclass or mathspec then
+ context.showmathcharactersstartentryclassspec()
+ if mathclass then
+ context.showmathcharactersentryclassname(mathclass,info.mathname or "no name")
+ end
+ if mathspec then
+ for i=1,#mathspec do
+ local mi = mathspec[i]
+ context.showmathcharactersentryclassname(mi.class,mi.name or "no name")
+ end
+ end
+ context.showmathcharactersstopentryclassspec()
+ end
+ if mathsymbol then
+ context.showmathcharactersentrysymbol(f_unicode(mathsymbol),mathsymbol)
+ end
+ if next_sizes then
+ local n, done = 0, { }
+ context.showmathcharactersstartnext()
+ while next_sizes do
+ n = n + 1
+ if done[next_sizes] then
+ context.showmathcharactersnextcycle(n)
+ break
+ else
+ done[next_sizes] = true
+ context.showmathcharactersnextentry(n,f_unicode(next_sizes),next_sizes)
+ next_sizes = characters[next_sizes]
+ v_variants = next_sizes.vert_variants or v_variants
+ h_variants = next_sizes.horiz_variants or h_variants
+ if next_sizes then
+ next_sizes = next_sizes.next
+ end
+ end
+ end
+ context.showmathcharactersstopnext()
+ if h_variants or v_variants then
+ context.showmathcharactersbetweennextandvariants()
+ end
+ end
+ if h_variants then
+ context.showmathcharactersstarthvariants()
+ for i=1,#h_variants do -- we might go top-down in the original
+ local vi = h_variants[i]
+ context.showmathcharactershvariantsentry(i,f_unicode(vi.glyph),vi.glyph)
+ end
+ context.showmathcharactersstophvariants()
+ elseif v_variants then
+ context.showmathcharactersstartvvariants()
+ for i=1,#v_variants do
+ local vi = v_variants[#v_variants-i+1]
+ context.showmathcharactersvvariantsentry(i,f_unicode(vi.glyph),vi.glyph)
+ end
+ context.showmathcharactersstopvvariants()
+ end
+ if slookups or mlookups then
+ local variants = { }
+ if slookups then
+ for lookupname, lookupdata in next, slookups do
+ local lookuptype = lookuptypes[lookupname]
+ if lookuptype == "substitution" then
+ variants[lookupdata] = "sub"
+ elseif lookuptype == "alternate" then
+ for i=1,#lookupdata do
+ variants[lookupdata[i]] = "alt"
+ end
+ end
+ end
+ end
+ if mlookups then
+ for lookupname, lookuplist in next, mlookups do
+ local lookuptype = lookuptypes[lookupname]
+ for i=1,#lookuplist do
+ local lookupdata = lookuplist[i]
+ local lookuptype = lookuptypes[lookupname]
+ if lookuptype == "substitution" then
+ variants[lookupdata] = "sub"
+ elseif lookuptype == "alternate" then
+ for i=1,#lookupdata do
+ variants[lookupdata[i]] = "alt"
+ end
+ end
+ end
+ end
+ end
+ context.showmathcharactersstartlookupvariants()
+ local i = 0
+ for variant, lookuptype in table.sortedpairs(variants) do
+ i = i + 1
+ context.showmathcharacterslookupvariant(i,f_unicode(variant),variant,lookuptype)
+ end
+ context.showmathcharactersstoplookupvariants()
+ end
+ context.showmathcharactersstopentry()
+ end
+ end
+ end
+ end
+ context.showmathcharactersstop()
+ end
+end
diff --git a/tex/context/modules/mkiv/s-math-characters.mkiv b/tex/context/modules/mkiv/s-math-characters.mkiv
new file mode 100644
index 000000000..3b273cb6c
--- /dev/null
+++ b/tex/context/modules/mkiv/s-math-characters.mkiv
@@ -0,0 +1,193 @@
+%D \module
+%D [ file=s-math-characters.mkiv, % was: s-fnt-25 and later s-mat-10
+%D version=2009.01.25,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Math Glyph Checking,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[math-characters]
+
+\registerctxluafile{s-math-characters}{}
+
+% context --jit --global --bodyfont=cambria --result=math-characters-cambria s-math-characters.mkiv
+% context --jit --global --bodyfont=modern --result=math-characters-modern s-math-characters.mkiv
+
+\startsetups s-math-characters:reset
+
+ \let\showmathcharactersstart \relax
+ \let\showmathcharactersstop \relax
+ \let\showmathcharactersreference \gobbleoneargument
+ \let\showmathcharactersstartentry \relax
+ \let\showmathcharactersstopentry \relax
+ \let\showmathcharactersentryhexdectit \gobblethreearguments
+ \let\showmathcharactersentrywdhtdpic \gobblefourarguments
+ \let\showmathcharactersentryresource \gobbleoneargument
+ \let\showmathcharactersstartnext \relax
+ \let\showmathcharactersnextentry \gobblethreearguments
+ \let\showmathcharactersnextcycle \gobbleonearguments
+ \let\showmathcharactersstopnext \relax
+ \let\showmathcharactersstarthvariants \relax
+ \let\showmathcharactershvariantsentry \gobblethreearguments
+ \let\showmathcharactersstophvariants \relax
+ \let\showmathcharactersstartvvariants \showmathcharactersstarthvariants
+ \let\showmathcharactersvvariantsentry \showmathcharactershvariantsentry
+ \let\showmathcharactersstopvvariants \showmathcharactersstophvariants
+ \let\showmathcharactersbetweennextandvariants\relax
+ \let\showmathcharactersstartentryclassspec \relax
+ \let\showmathcharactersstopentryclassspec \relax
+ \let\showmathcharactersentryclassname \gobbletwoarguments
+ \let\showmathcharactersentrysymbol \gobbletwoarguments
+ \let\showmathcharactersstartlookupvariants \relax
+ \let\showmathcharacterslookupvariant \gobblefourarguments
+ \let\showmathcharactersstoplookupvariants \relax
+ \let\showmathcharacterssetrange \gobblethreearguments
+
+\stopsetups
+
+\directsetup{s-math-characters:reset}
+
+\startsetups s-math-characters:default
+
+ \directsetup{s-math-characters:reset}
+
+ \unexpanded\def\showmathcharactersstartentry {\blank\begingroup\raggedright}
+ \unexpanded\def\showmathcharactersstopentry {\endgroup\blank}
+
+ \def\showmathcharactersentryhexdectit##1##2##3%
+ {##1:\space{\char##2}\space\ruledhbox{\char##2}\space##3\par
+ \advance\leftskip\emwidth\relax}
+
+ \def\showmathcharactersentrywdhtdpic##1##2##3##4%
+ {width:\space##1,\space height:\space##2,\space depth:\space##3,\space italic:\space##4\par}
+
+ \def\showmathcharactersentryresource##1%
+ {virtual:\space##1\par}
+
+ \def\showmathcharactersstartnext
+ {\par\begingroup\hangindent\emwidth\hangafter-\plushundred\hskip-\emwidth
+ next:\space}
+
+ \def\showmathcharactersnextentry##1##2##3%
+ {\ifnum##1>\plusone \space=>\space\fi##2~\ruledhbox{\char##3}}
+
+ \def\showmathcharactersnextcycle##1%
+ {\ifnum##1>\plusone \space=>\space\fi cycle}
+
+ \def\showmathcharactersstopnext
+ {\par\endgroup}
+
+ \def\showmathcharactersstarthvariants
+ {\par\begingroup\hangindent\emwidth\hangafter-\plushundred\hskip-\emwidth
+ variants:\space}
+
+ \def\showmathcharactershvariantsentry##1##2##3%
+ {\ifnum##1>\plusone \space=>\space\fi##2~\ruledhbox{\char##3}}
+
+ \def\showmathcharactersstophvariants
+ {\par\endgroup}
+
+ \let\showmathcharactersstartvvariants\showmathcharactersstarthvariants
+ \let\showmathcharactersvvariantsentry\showmathcharactershvariantsentry
+ \let\showmathcharactersstopvvariants \showmathcharactersstophvariants
+
+ \def\showmathcharactersbetweennextandvariants
+ {}% \space=>\space}
+
+ \def\showmathcharactersentryclassname##1##2%
+ {mathclass:\space##1,\space mathname:\space##2\par}
+
+ \def\showmathcharactersentrysymbol##1##2%
+ {mathsymbol:\space##1~\ruledhbox{\char##2}\par}
+
+ \def\showmathcharactersstartlookupvariants
+ {\par\begingroup\hangindent\emwidth\hangafter-\plushundred\hskip-\emwidth
+ lookupvariants:\space}
+
+ \def\showmathcharacterslookupvariant##1##2##3##4%
+ {\ifnum##1>\plusone,\space\fi##2:~{\char##3}\space(##4)}
+
+ \def\showmathcharactersstoplookupvariants
+ {\par\endgroup}
+
+\stopsetups
+
+\unprotect
+
+\unexpanded\def\enableshowmathfontvirtual
+ {\ctxlua{fonts.constructors.autocleanup=false}}
+
+\unexpanded\def\showmathfontcharacters
+ {\dosingleempty\module_math_characters_show}
+
+\def\module_math_characters_show[#1]%
+ {\begingroup
+ \getdummyparameters
+ [\c!bodyfont=,
+ \c!list=,
+ \c!check=,
+ \c!alternative=default,
+ \c!option=\v!all,
+ #1]%
+ \directsetup{s-math-characters:\dummyparameter\c!alternative}%
+ \doifelsenothing{\dummyparameter\c!bodyfont}
+ {\definedfont[MathRoman*math-text]}
+ {\definedfont[\dummyparameter\c!bodyfont]}%
+ \dontcomplain
+ \ctxlua{moduledata.math.characters.showlist {
+ number = false,
+ check = "\dummyparameter\c!check",
+ list = "\dummyparameter\c!list",
+ option = "\dummyparameter\c!option",
+ }}%
+ \endgroup}
+
+\protect
+
+\stopmodule
+
+\continueifinputfile{s-math-characters.mkiv}
+
+\setuplayout
+ [width=middle,
+ height=middle,
+ topspace=15mm,
+ backspace=15mm,
+ bottomspace=15mm,
+ header=1cm,
+ headerdistance=0.5cm,
+ footer=0pt]
+
+\starttext
+
+ \doifelse {\getdocumentargument{bodyfont}} {} {
+
+ \setupbodyfont[cambria, 12pt]
+ % \setupbodyfont[modern, 12pt]
+ % \setupbodyfont[lmvirtual, 12pt]
+ % \setupbodyfont[pxvirtual, 12pt]
+ % \setupbodyfont[txvirtual, 12pt]
+ % \setupbodyfont[palatino, 10pt]
+ % \setupbodyfont[mathtimes, 12pt]
+ % \setupbodyfont[stix, 12pt]
+ % \setupbodyfont[xits, 12pt]
+ % \setupbodyfont[lucida, 12pt]
+ % \setupbodyfont[lucidanova,12pt]
+ % \setupbodyfont[pagella, 12pt]
+ % \setupbodyfont[bonum, 12pt]
+
+ } {
+
+ \normalexpanded{\setupbodyfont[\getdocumentargument{bodyfont},12pt]}
+
+ }
+
+ \showmathfontcharacters
+
+\stoptext
+
diff --git a/tex/context/modules/mkiv/s-math-coverage.lua b/tex/context/modules/mkiv/s-math-coverage.lua
new file mode 100644
index 000000000..3c6080dc3
--- /dev/null
+++ b/tex/context/modules/mkiv/s-math-coverage.lua
@@ -0,0 +1,197 @@
+if not modules then modules = { } end modules ['s-math-coverage'] = {
+ version = 1.001,
+ comment = "companion to s-math-coverage.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local utfchar, utfbyte = utf.char, utf.byte
+local formatters, lower = string.formatters, string.lower
+local concat = table.concat
+local sortedhash = table.sortedhash
+
+moduledata.math = moduledata.math or { }
+moduledata.math.coverage = moduledata.math.coverage or { }
+
+local context = context
+
+local ctx_NC = context.NC
+local ctx_NR = context.NR
+local ctx_HL = context.HL
+
+local ctx_startmixedcolumns = context.startmixedcolumns
+local ctx_stopmixedcolumns = context.stopmixedcolumns
+local ctx_setupalign = context.setupalign
+local ctx_starttabulate = context.starttabulate
+local ctx_stoptabulate = context.stoptabulate
+local ctx_rawmathematics = context.formatted.rawmathematics
+local ctx_mathematics = context.formatted.mathematics
+local ctx_startimath = context.startimath
+local ctx_stopimath = context.stopimath
+local ctx_setmathattribute = context.setmathattribute
+local ctx_underbar = context.underbar
+local ctx_getglyph = context.getglyph
+
+local styles = mathematics.styles
+local alternatives = mathematics.alternatives
+local charactersets = mathematics.charactersets
+
+local getboth = mathematics.getboth
+local remapalphabets = mathematics.remapalphabets
+
+local chardata = characters.data
+local superscripts = characters.superscripts
+local subscripts = characters.subscripts
+
+context.writestatus("math coverage","underline: not remapped")
+
+function moduledata.math.coverage.showalphabets()
+ ctx_starttabulate { "|lT|l|Tl|" }
+ for i=1,#styles do
+ local style = styles[i]
+ for i=1,#alternatives do
+ local alternative = alternatives[i]
+ for _, alphabet in sortedhash(charactersets) do
+ ctx_NC()
+ if i == 1 then
+ context("%s %s",style,alternative)
+ end
+ ctx_NC()
+ ctx_startimath()
+ ctx_setmathattribute(style,alternative)
+ for i=1,#alphabet do
+ local letter = alphabet[i]
+ local id = getboth(style,alternative)
+ local unicode = remapalphabets(letter,id)
+ if not unicode then
+ ctx_underbar(utfchar(letter))
+ elseif unicode == letter then
+ context(utfchar(unicode))
+ else
+ context(utfchar(unicode))
+ end
+ end
+ ctx_stopimath()
+ ctx_NC()
+ local first = alphabet[1]
+ local last = alphabet[#alphabet]
+ local id = getboth(style,alternative)
+ local f_unicode = remapalphabets(first,id) or utfbyte(first)
+ local l_unicode = remapalphabets(last,id) or utfbyte(last)
+ context("%05X - %05X",f_unicode,l_unicode)
+ ctx_NC()
+ ctx_NR()
+ end
+ end
+ end
+ ctx_stoptabulate()
+end
+
+function moduledata.math.coverage.showcharacters()
+ ctx_startmixedcolumns { balance = "yes" }
+ ctx_setupalign { "nothyphenated" }
+ ctx_starttabulate { "|T|i2|Tpl|" }
+ for u, d in sortedhash(chardata) do
+ local mathclass = d.mathclass
+ local mathspec = d.mathspec
+ if mathclass or mathspec then
+ ctx_NC()
+ context("%05X",u)
+ ctx_NC()
+ ctx_getglyph("MathRoman",u)
+ ctx_NC()
+ if mathspec then
+ local t = { }
+ for i=1,#mathspec do
+ t[mathspec[i].class] = true
+ end
+ t = table.sortedkeys(t)
+ context("% t",t)
+ else
+ context(mathclass)
+ end
+ ctx_NC()
+ ctx_NR()
+ end
+ end
+ ctx_stoptabulate()
+ ctx_stopmixedcolumns()
+end
+
+-- This is a somewhat tricky table as we need to bypass the math machinery.
+
+function moduledata.math.coverage.showscripts()
+ ctx_starttabulate { "|cT|c|cT|c|c|c|l|" }
+ for k, v in sortedhash(table.merged(superscripts,subscripts)) do
+ local ck = utfchar(k)
+ local cv = utfchar(v)
+ local ss = superscripts[k] and "^" or "_"
+ ctx_NC() context("%05X",k)
+ ctx_NC() context(ck)
+ ctx_NC() context("%05X",v)
+ ctx_NC() context(cv)
+ ctx_NC() ctx_rawmathematics("x%s = x%s%s",ck,ss,cv)
+ ctx_NC() ctx_mathematics("x%s = x%s%s",ck,ss,cv)
+ ctx_NC() context(lower(chardata[k].description))
+ ctx_NC() ctx_NR()
+ end
+ ctx_stoptabulate()
+end
+
+-- Handy too.
+
+function moduledata.math.coverage.showbold()
+ ctx_starttabulate { "|lT|cm|lT|cm|lT|" }
+ for k, v in sortedhash(mathematics.boldmap) do
+ ctx_NC() context("%U",k)
+ ctx_NC() context("%c",k)
+ ctx_NC() context("%U",v)
+ ctx_NC() context("%c",v)
+ ctx_NC() context(chardata[k].description)
+ ctx_NC() ctx_NR()
+ end
+ ctx_stoptabulate()
+end
+
+-- function moduledata.math.coverage.showentities()
+-- ctx_startmixedcolumns { balance = "yes" }
+-- ctx_starttabulate { "|Tl|c|Tl|" }
+-- for k, v in sortedhash(characters.entities) do
+-- local b = utf.byte(v)
+-- local d = chardata[b]
+-- local m = d.mathname
+-- local c = d.contextname
+-- local s = ((m and "\\"..m) or (c and "\\".. c) or v) .. "{}{}{}"
+-- ctx_NC()
+-- context("%U",b)
+-- ctx_NC()
+-- ctx_mathematics(s)
+-- ctx_NC()
+-- context(k)
+-- ctx_NC()
+-- ctx_NR()
+-- end
+-- ctx_stoptabulate()
+-- ctx_stopmixedcolumns()
+-- end
+
+function moduledata.math.coverage.showentities()
+ ctx_startmixedcolumns { balance = "yes" }
+ ctx_starttabulate { "|T||T|T|" }
+ for k, v in sortedhash(characters.entities) do
+ local d = chardata[v]
+ if d then
+ local m = d.mathclass or d.mathspec
+ local u = d.unicodeslot
+ ctx_NC() context(m and "m" or "t")
+ ctx_NC() ctx_getglyph("MathRoman",u)
+ ctx_NC() context("%05X",u)
+ ctx_NC() context(k)
+ ctx_NC() ctx_NR()
+ end
+ end
+ ctx_stoptabulate()
+ ctx_stopmixedcolumns()
+end
+
diff --git a/tex/context/modules/mkiv/s-math-coverage.mkiv b/tex/context/modules/mkiv/s-math-coverage.mkiv
new file mode 100644
index 000000000..e318c9eff
--- /dev/null
+++ b/tex/context/modules/mkiv/s-math-coverage.mkiv
@@ -0,0 +1,38 @@
+%D \module
+%D [ file=s-math-coverage, % s-fnt-33, s-fnt-32
+%D version=2011.05.10, % and older
+%D title=\CONTEXT\ Style File,
+%D subtitle=Show Math Coverage,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[math-coverage]
+
+\registerctxluafile{s-math-coverage}{}
+
+\installmodulecommandluasingle \showmathalphabets {moduledata.math.coverage.showalphabets}
+\installmodulecommandluasingle \showmathcharacters{moduledata.math.coverage.showcharacters}
+\installmodulecommandluasingle \showmathscripts {moduledata.math.coverage.showscripts}
+\installmodulecommandluasingle \showmathbold {moduledata.math.coverage.showbold}
+\installmodulecommandluasingle \showmathentities {moduledata.math.coverage.showentities}
+
+\stopmodule
+
+\continueifinputfile{s-math-coverage.mkiv}
+
+\usemodule[art-01]
+
+\starttext
+
+ \showmathalphabets \page
+ \showmathcharacters \page
+ \showmathscripts \page
+ \showmathbold \page
+ \showmathentities \page
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-math-extensibles.mkiv b/tex/context/modules/mkiv/s-math-extensibles.mkiv
new file mode 100644
index 000000000..f9ff8547a
--- /dev/null
+++ b/tex/context/modules/mkiv/s-math-extensibles.mkiv
@@ -0,0 +1,145 @@
+%D \module
+%D [ file=s-math-extensibles.mkiv,
+%D version=2013.02.03,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Math Stackers Checking,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This module provides a macro that will typeset a table of (horizontal)
+%D extensibles including some tracing. You can set up the \type {demo}
+%D stacker category to tweak things.
+%D
+%D \starttyping
+%D \showmathextensibles[alternative=a|b]
+%D \stoptyping
+
+\startmodule[math-extensibles]
+
+\unprotect
+
+\definemathstackers
+ [demo]
+ [math]
+ [\c!offset=\v!max]
+
+\installcorenamespace{modulemathextensibles}
+\installcorenamespace{modulemathextensiblesalternative}
+
+\installdirectsetuphandler \??modulemathextensibles {modulemathextensibles}
+
+\setupmodulemathextensibles
+ [\c!alternative=\v!a]
+
+\unexpanded\def\showmathextensibles
+ {\dosingleempty\module_math_extensibles_show_all}
+
+\def\module_math_extensibles_show_all[#1]%
+ {\begingroup
+ \setupcurrentmodulemathextensibles[#1]%
+ \expandnamespacevalue\??modulemathextensiblesalternative\c!alternative\v!a
+ \endgroup}
+
+\def\modulemathextensiblesalternativea#1#2#3%
+ {\NC U+#1
+ \NC \filledhboxm{\math{\char"#1}}
+ \NC \hbox{\math{\mathextensible[demo]{"#1}{top}{bottom}}}
+ \NC \hbox{\math{\mathextensible[demo]{"#1}{}{bottom}}}
+ \NC \hbox{\math{\mathextensible[demo]{"#1}{top}{}}}
+ \NC \nohyphens \veryraggedright #2
+ \NC\NR}
+
+\setvalue{\??modulemathextensiblesalternative\v!a}%
+ {\enabletrackers[math.stackers.texts]
+ \starttabulate[|Tl|l|l|l|l|Tp|]
+ \ctxlua { moduledata.math.extensibles.show {
+ command = "modulemathextensiblesalternativea",
+ } }
+ \stoptabulate
+ \disabletrackers[math.stackers.texts]}
+
+\def\modulemathextensiblesalternativeb#1#2#3%
+ {\NC U+#1
+ \NC \math{\char"#1}
+ \NC \nohyphens \veryraggedright #3
+ \NC \NR}
+
+\setvalue{\??modulemathextensiblesalternative\v!b}%
+ {\enabletrackers[math.stackers.texts]
+ \starttabulate[|Tl|l|Tp|]
+ \ctxlua { moduledata.math.extensibles.show {
+ command = "modulemathextensiblesalternativeb",
+ sparse = true,
+ } }
+ \stoptabulate
+ \disabletrackers[math.stackers.texts]}
+
+\startluacode
+ moduledata.math = moduledata.math or { }
+ moduledata.math.extensibles = moduledata.math.extensibles or { }
+
+ function moduledata.math.extensibles.show(settings)
+ local command = settings.command
+ local sparse = settings.sparse
+ for k, v in table.sortedhash(characters.data) do
+ local mathextensible = v.mathextensible
+ if mathextensible == "r" or mathextensible == "l" or mathextensible == "h" then
+ local names = { }
+ local mathname = v.mathname
+ if mathname then
+ names[#names+1] = v.mathclass .. ":" .. mathname
+ end
+ local mathspec = v.mathspec
+ if mathspec then
+ for i=1,#mathspec do
+ local v = mathspec[i]
+ names[#names+1] = v.class .. ":" .. v.name
+ end
+ end
+ local mathfiller = v.mathfiller
+ if mathfiller then
+ names[#names+1] = "filler:" .. mathfiller
+ end
+ if not sparse or #names > 0 then
+ context[command](string.format("%04X",k),v.description,table.concat(names," "))
+ end
+ end
+ end
+ end
+\stopluacode
+
+\protect
+
+\stopmodule
+
+\continueifinputfile{s-math-extensibles.mkiv}
+
+\setuplayout
+ [width=middle,
+ height=middle,
+ footer=0cm,
+ backspace=1.5cm,
+ topspace=1.5cm]
+
+\setuphead[chapter][style=\bfc]
+\setuphead[section][style=\bfa]
+
+\starttext
+
+ \dowith {a,b} {
+
+ \page \title {Latin Modern} \showmathextensibles[alternative=#1]
+ \page \setupbodyfont[pagella] \title {Pagella} \showmathextensibles[alternative=#1]
+ \page \setupbodyfont[termes] \title {Termes} \showmathextensibles[alternative=#1]
+ \page \setupbodyfont[dejavu] \title {Xits} \showmathextensibles[alternative=#1]
+ \page \setupbodyfont[cambria] \title {Cambria} \showmathextensibles[alternative=#1]
+ \page \setupbodyfont[lucidaot] \title {Lucida} \showmathextensibles[alternative=#1]
+
+ }
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-math-parameters.lua b/tex/context/modules/mkiv/s-math-parameters.lua
new file mode 100644
index 000000000..8e8c15a2d
--- /dev/null
+++ b/tex/context/modules/mkiv/s-math-parameters.lua
@@ -0,0 +1,135 @@
+if not modules then modules = { } end modules ['s-math-coverage'] = {
+ version = 1.001,
+ comment = "companion to s-math-coverage.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.math = moduledata.math or { }
+moduledata.math.parameters = moduledata.math.parameters or { }
+
+local tables = utilities.tables.definedtable("math","tracing","spacing","tables")
+
+tables.styleaxis = {
+ "ord", "op", "bin", "rel", "open", "close", "punct", "inner",
+}
+
+tables.parameters = {
+ "quad", "axis", "operatorsize",
+ "overbarkern", "overbarrule", "overbarvgap",
+ "underbarkern", "underbarrule", "underbarvgap",
+ "radicalkern", "radicalrule", "radicalvgap",
+ "radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise",
+ "stackvgap", "stacknumup", "stackdenomdown",
+ "fractionrule", "fractionnumvgap", "fractionnumup",
+ "fractiondenomvgap", "fractiondenomdown", "fractiondelsize",
+ "limitabovevgap", "limitabovebgap", "limitabovekern",
+ "limitbelowvgap", "limitbelowbgap", "limitbelowkern",
+ "underdelimitervgap", "underdelimiterbgap",
+ "overdelimitervgap", "overdelimiterbgap",
+ "subshiftdrop", "supshiftdrop", "subshiftdown",
+ "subsupshiftdown", "subtopmax", "supshiftup",
+ "supbottommin", "supsubbottommax", "subsupvgap",
+ "spaceafterscript", "connectoroverlapmin",
+}
+
+tables.styles = {
+ "display",
+ "text",
+ "script",
+ "scriptscript",
+}
+
+function tables.stripmu(str)
+ str = string.gsub(str,"mu","")
+ str = string.gsub(str," ","")
+ str = string.gsub(str,"plus","+")
+ str = string.gsub(str,"minus","-")
+ return str
+end
+
+function tables.strippt(old)
+ local new = string.gsub(old,"pt","")
+ if new ~= old then
+ new = string.format("%0.4f",tonumber(new))
+ end
+ return new
+end
+
+function moduledata.math.parameters.showspacing()
+
+ local styles = tables.styles
+ local styleaxis = tables.styleaxis
+
+ context.starttabulate { "|Tl|Tl|" .. string.rep("Tc|",(#styles*2)) }
+ context.HL()
+ context.NC()
+ context.NC()
+ context.NC()
+ for i=1,#styles do
+ context.bold(styles[i])
+ context.NC()
+ context.bold("(cramped)")
+ context.NC()
+ end
+ context.NR()
+ context.HL()
+ for i=1,#styleaxis do
+ -- print(key,tex.getmath(key,"text"))
+ local one = styleaxis[i]
+ for j=1,#styleaxis do
+ local two = styleaxis[j]
+ context.NC()
+ if j == 1 then
+ context.bold(one)
+ end
+ context.NC()
+ context.bold(two)
+ context.NC()
+ for i=1,#styles do
+ context("\\ctxlua{context(math.tracing.spacing.tables.stripmu('\\the\\Umath%s%sspacing\\%sstyle'))}",one,two,styles[i])
+ context.NC()
+ context("\\ctxlua{context(math.tracing.spacing.tables.stripmu('\\the\\Umath%s%sspacing\\cramped%sstyle'))}",one,two,styles[i])
+ context.NC()
+ end
+ context.NR()
+ end
+ end
+ context.stoptabulate()
+end
+
+function moduledata.math.parameters.showparameters()
+
+ local styles = tables.styles
+ local parameters = tables.parameters
+
+ context.starttabulate { "|l|" .. string.rep("Tc|",(#styles*2)) }
+ context.HL()
+ context.NC()
+ context.NC()
+ for i=1,#styles do
+ context.bold(styles[i])
+ context.NC()
+ context.bold("(cramped)")
+ context.NC()
+ end
+ context.NR()
+ context.HL()
+ for i=1,#parameters do
+ local parameter = parameters[i]
+ -- print(parameter,tex.getmath(parameter,"text"))
+ context.NC()
+ context.type(parameter)
+ context.NC()
+ for i=1,#styles do
+ context("\\ctxlua{context(math.tracing.spacing.tables.strippt('\\the\\Umath%s\\%sstyle'))}",parameter,styles[i])
+ context.NC()
+ context("\\ctxlua{context(math.tracing.spacing.tables.strippt('\\the\\Umath%s\\cramped%sstyle'))}",parameter,styles[i])
+ context.NC()
+ end
+ context.NR()
+ end
+ context.stoptabulate()
+
+end
diff --git a/tex/context/modules/mkiv/s-math-parameters.mkiv b/tex/context/modules/mkiv/s-math-parameters.mkiv
new file mode 100644
index 000000000..f2fde5d83
--- /dev/null
+++ b/tex/context/modules/mkiv/s-math-parameters.mkiv
@@ -0,0 +1,41 @@
+%D \module
+%D [ file=s-math-parameters.mkiv,
+%D version=2012.12.05,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Show Math Parameters,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[math-parameters]
+
+\registerctxluafile{s-math-parameters}{}
+
+\installmodulecommandluasingle \showmathspacing {moduledata.math.parameters.showspacing}
+\installmodulecommandluasingle \showmathparameters {moduledata.math.parameters.showparameters}
+
+\stopmodule
+
+\continueifinputfile{s-math-parameters.mkiv}
+
+\setuplayout
+ [width=middle,
+ height=middle,
+ backspace=1cm,
+ topspace=1cm,
+ footer=0pt,
+ header=0pt]
+
+\setupbodyfont
+ [dejavu,8pt]
+
+\starttext
+
+ \showmathspacing \page
+ \showmathparameters \page
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-math-repertoire.mkiv b/tex/context/modules/mkiv/s-math-repertoire.mkiv
new file mode 100644
index 000000000..230eb513e
--- /dev/null
+++ b/tex/context/modules/mkiv/s-math-repertoire.mkiv
@@ -0,0 +1,487 @@
+%D \module
+%D [ file=s-math-parameters.mkiv, % was s-mat-12.mkiv
+%D version=2012.06.06, % whatever, probably a bit earlier
+%D title=\CONTEXT\ Style File,
+%D subtitle=Show Math Character Repertoire,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\usemodule[s][math-characters]
+
+% context --jit --global --bodyfont=lucidanova --result=math-repertoire-lucidanova s-math-repertoire.mkiv
+% context --jit --global --bodyfont=cambria --result=math-repertoire-cambria s-math-repertoire.mkiv
+% context --jit --global --bodyfont=xits --result=math-repertoire-xits s-math-repertoire.mkiv
+% context --jit --global --bodyfont=modern --result=math-repertoire-modern s-math-repertoire.mkiv
+% context --jit --global --bodyfont=pagella --result=math-repertoire-pagella s-math-repertoire.mkiv
+% context --jit --global --bodyfont=termes --result=math-repertoire-termes s-math-repertoire.mkiv
+% context --jit --global --bodyfont=bonum --result=math-repertoire-bonum s-math-repertoire.mkiv
+
+\startmodule[math-repertoire]
+
+% layout
+
+\definecolor[backgroundcolor] [r=.6,g=.6]
+\definecolor[backgroundcolorx][r=.6]
+\definecolor[backgroundcolory][g=.6]
+\definecolor[baselinecolor] [a=1,t=.5,s=.6]
+\definecolor[charactercolor] [b=.6]
+\definecolor[pagecolor] [s=.1]
+\definecolor[nonecolor] [s=.5]
+\definecolor[textcolor] [s=.9]
+
+% \setuppapersize[HD]
+%
+% \setuplayout
+% [page]
+
+\setuppapersize
+ [HD+]
+
+\setuplayout
+ [backspace=0pt,
+ topspace=0pt,
+ bottomspace=120pt, %1200-1080
+ bottom=24pt,
+ bottomdistance=5mm,
+ header=0pt,
+ footer=0pt,
+ width=middle,
+ height=middle]
+
+\definelayer
+ [page]
+
+\setuplayer
+ [page]
+ [width=\textwidth,
+ height=\textheight]
+
+\setupbackgrounds
+ [page]
+ [background=color,
+ backgroundcolor=pagecolor]
+
+\setupbackgrounds
+ [text][text]
+ [bottomframe=on,
+ framecolor=textcolor,
+ rulethickness=0.025ex]
+
+\setupmakeup
+ [standard]
+ [pagestate=start]
+
+\setupinteraction
+ [state=start,
+ menu=on]
+
+\defineinteractionmenu
+ [ALPHABETS]
+ [bottom]
+
+\defineinteractionmenu
+ [alphabets]
+ [bottom]
+
+\defineinteractionmenu
+ [symbols]
+ [bottom]
+
+\setupinteractionmenu
+ [bottom]
+ [before=\vfill,
+ after=\vfill,
+ left=\hfill,
+ right=\hfill,
+ style=\tt\bf,
+ distance=0pt,
+ color=textcolor,
+ contrastcolor=nonecolor]
+
+\def\showmathcharacterssetrange#1#2#3%
+ {\writestatus{range}{#1: \unihex{#2} - \unihex{#3}}%
+ \ifcase#2\relax
+ \definereference[#1][notpresent]%
+ \else\ifcase#3\relax
+ \definereference[#1][notpresent]%
+ \else
+ \normalexpanded{\definereference[#1][\unihex{#2}]}%
+ \fi\fi}
+
+\startinteractionmenu[bottom]
+ \startgot [firstpage] first \stopgot \quad
+ \startgot [deltapage(-100)] -100 \stopgot \quad
+ \startgot [deltapage(-10)] -10 \stopgot \quad
+ \startgot [previouspage] previous \stopgot \quad
+ \startgot [nextpage] next \stopgot \quad
+ \startgot [deltapage(+10)] +10 \stopgot \quad
+ \startgot [deltapage(+100)] +100 \stopgot \quad
+ \startgot [lastpage] last \stopgot
+\stopinteractionmenu
+
+\startinteractionmenu[ALPHABETS]
+ \startgot [U+00041] NORMAL \stopgot \quad
+ \startgot [U+1D400] BOLD \stopgot \quad
+ \startgot [U+1D434] ITALIC \stopgot \quad
+ \startgot [U+1D468] BOLDITALIC \stopgot \quad
+ \startgot [U+1D49C] SCRIPT \stopgot \quad
+ \startgot [U+1D4D0] BOLDSCRIPT \stopgot \quad
+ \startgot [U+1D504] FRAKTUR \stopgot \quad
+ \startgot [U+1D538] DOUBLESTRUCK \stopgot \quad
+ \startgot [U+1D56C] BOLDFRAKTUR \stopgot \quad
+ \startgot [U+1D5A0] SS NORMAL \stopgot \quad
+ \startgot [U+1D5D4] SS BOLD \stopgot \quad
+ \startgot [U+1D608] SS ITALIC \stopgot \quad
+ \startgot [U+1D63C] SS BOLDITALIC \stopgot \quad
+ \startgot [U+1D670] MONOSPACE \stopgot \quad
+ \startgot [U+00391] GRK NORMAL \stopgot \quad
+ \startgot [U+1D6A8] GRK BOLD \stopgot \quad
+ \startgot [U+1D6E2] GRK ITALIC \stopgot \quad
+ \startgot [U+1D71C] GRK BOLDITALIC \stopgot \quad
+ \startgot [U+1D756] GRK SS BOLD \stopgot \quad
+ \startgot [U+1D790] GRK SS BOLDITALIC \stopgot
+\stopinteractionmenu
+
+\startinteractionmenu[alphabets]
+ \startgot [U+00061] normal \stopgot \quad
+ \startgot [U+1D41A] bold \stopgot \quad
+ \startgot [U+1D44E] italic \stopgot \quad
+ \startgot [U+1D482] bolditalic \stopgot \quad
+ \startgot [U+1D4B6] script \stopgot \quad
+ \startgot [U+1D4EA] boldscript \stopgot \quad
+ \startgot [U+1D51E] fraktur \stopgot \quad
+ \startgot [U+1D552] doublestruck \stopgot \quad
+ \startgot [U+1D586] boldfraktur \stopgot \quad
+ \startgot [U+1D5BA] ss normal \stopgot \quad
+ \startgot [U+1D5EE] ss bold \stopgot \quad
+ \startgot [U+1D622] ss italic \stopgot \quad
+ \startgot [U+1D656] ss bolditalic \stopgot \quad
+ \startgot [U+1D68A] monospace \stopgot \quad
+ \startgot [U+003B1] grk normal \stopgot \quad
+ \startgot [U+1D6C2] grk bold \stopgot \quad
+ \startgot [U+1D6FC] grk italic \stopgot \quad
+ \startgot [U+1D736] grk bolditalic \stopgot \quad
+ \startgot [U+1D770] grk ss bold \stopgot \quad
+ \startgot [U+1D7AA] grk ss bolditalic \stopgot
+\stopinteractionmenu
+
+% \startinteractionmenu[symbols]
+% \startgot [U+00030] dig normal \stopgot \quad
+% \startgot [U+1D7CE] dig bold \stopgot \quad
+% \startgot [U+1D7D8] dig doublestruck \stopgot \quad
+% \startgot [U+1D7E2] dig ss normal \stopgot \quad
+% \startgot [U+1D7EC] dig ss bold \stopgot \quad
+% \startgot [U+1D7F6] dig monospace \stopgot \quad
+% \startgot [U+02200] operators \stopgot \quad
+% \startgot [U+02701] symbols a \stopgot \quad
+% \startgot [U+02901] symbols b \stopgot \quad
+% \startgot [U+02A00] supplemental \stopgot \quad
+% \startgot [U+027F0] arrows a \stopgot \quad
+% \startgot [U+02900] arrows b \stopgot \quad
+% \startgot [U+1F800] arrows c \stopgot
+% \stopinteractionmenu
+
+\startinteractionmenu[symbols]
+ \startgot [U+00030] dig normal \stopgot \quad
+ \startgot [U+1D7CE] dig bold \stopgot \quad
+ \startgot [U+1D7D8] dig doublestruck \stopgot \quad
+ \startgot [U+1D7E2] dig ss normal \stopgot \quad
+ \startgot [U+1D7EC] dig ss bold \stopgot \quad
+ \startgot [U+1D7F6] dig monospace \stopgot \quad
+ \startgot [U+02200] operators \stopgot \quad
+ \startgot [miscellaneousmathematicalsymbolsa] symbols a \stopgot \quad
+ \startgot [miscellaneousmathematicalsymbolsb] symbols b \stopgot \quad
+ \startgot [supplementalmathematicaloperators] supplemental \stopgot \quad
+ \startgot [supplementalarrowsa] arrows a \stopgot \quad
+ \startgot [supplementalarrowsb] arrows b \stopgot \quad
+ \startgot [supplementalarrowsc] arrows c \stopgot \quad
+ \removeunwantedspaces
+\stopinteractionmenu
+
+\defineframed
+ [somedata]
+ [background=color,
+ backgroundcolor=textcolor,
+ %framecolor=charactercolor,
+ %rulethickness=1pt,
+ frame=off,
+ offset=1ex]
+
+% helpers
+
+\unexpanded\def\showmathcharacterstxt#1%
+ {{\tttf#1}}
+
+\unexpanded\def\showmathcharacterschr#1#2%
+ {\iffontchar\font#2\relax
+ \scale
+ [sx=#1,sy=#1]
+ {\dontleavehmode
+ \begingroup
+ \setbox\scratchbox\hbox{\charactercolor\char#2}%
+ \scratchdimen\wd\scratchbox
+ \ifdim\scratchdimen>\zeropoint
+ \backgroundline[backgroundcolor]{\box\scratchbox}%
+ \else\ifdim\scratchdimen<\zeropoint
+ \scratchdimen-\scratchdimen
+ \setbox\scratchbox\hbox to \scratchdimen{\hss\charactercolor\char#2}%
+ \backgroundline[backgroundcolorx]{\box\scratchbox}%
+ \else
+ \setbox\scratchbox\hbox to 1em{\hss\charactercolor\char#2}%
+ \scratchdimen\wd\scratchbox
+ \backgroundline[backgroundcolory]{\box\scratchbox}%
+ \fi\fi
+ \hskip-\scratchdimen
+ \baselinecolor\vrule width \scratchdimen height .05ex depth .05ex
+ \endgroup}%
+ \fi}
+
+\unexpanded\def\showmathcharactersmth#1#2%
+ {\setbox\scratchbox\hbox{\showmathcharacterschr{#1}{#2}}%
+ \ht\scratchbox\strutht
+ \dp\scratchbox\strutdp
+ \box\scratchbox}
+
+\let\showmathcharactersbodyfonts\empty
+
+\unexpanded\def\showmathcharactersbodyfontentry#1#2%
+ {\somedata
+ [align={none,middle},background=,height=5cm]
+ {\begingroup
+ \switchtobodyfont[#2,12pt]%
+ \definedfont[MathRoman*math-text]%
+ \showmathcharactersmth{10}{#1}%
+ \endgroup
+ \vfilll
+ \doifelsemode{crosslink}
+ {\goto{\strut\textcolor\showmathcharacterstxt{#2}}[#2::#1]}%
+ {\strut\textcolor\showmathcharacterstxt{#2}}}%
+ \hskip1ex}
+
+\unexpanded\def\showmathcharacterssetbodyfonts#1%
+ {\edef\showmathcharactersbodyfonts{#1}%
+ \def\doshowmathcharacterssetbodyfonts##1{\setupbodyfont[##1,12pt]}%
+ \processcommacommand[\showmathcharactersbodyfonts]\doshowmathcharacterssetbodyfonts}
+
+\unexpanded\def\showmathcharactersbodyfontschars#1%
+ {\processcommacommand[\showmathcharactersbodyfonts]{\showmathcharactersbodyfontentry{#1}}}
+
+% main
+
+\unexpanded\def\showmathcharactersstart
+ {\starttext
+ \startstandardmakeup % we use baselinecolor so that we have a transparency on page 1
+ \setupalign[middle]
+ \vfil
+ \dontleavehmode \scale[height=.3\textheight]{\strut\color[textcolor]{\fontclass}}
+ \vfil
+ \dontleavehmode \scale[height=.1\textheight]{\strut\color[baselinecolor]{\currentdate}}
+ \vfil
+ \vfil
+ \stopstandardmakeup}
+
+\unexpanded\def\showmathcharactersstop
+ {\stoptext}
+
+% entry
+
+\unexpanded\def\showmathcharactersstartentry
+ {\startstandardmakeup}
+
+\unexpanded\def\showmathcharactersstopentry
+ {\tightlayer[page]
+ \stopstandardmakeup}
+
+\unexpanded\def\showmathcharactersreference#1%
+ {\setlayer[page]{\pagereference[#1]}}
+
+\unexpanded\def\showmathcharactersentryhexdectit#1#2#3%
+ {\setlayer
+ [page]
+ [preset=middletop,voffset=5mm]
+ {\somedata[height=1cm]{\showmathcharacterstxt{#1}}}
+ \setlayer
+ [page]
+ [preset=middle,y=2cm]
+ {\showmathcharactersmth{25}{#2}}
+ \setlayer
+ [page]
+ [preset=righttop,offset=5mm]
+ {\somedata[height=1cm]{\showmathcharacterstxt{#3}}}
+ \doifsomething\showmathcharactersbodyfonts
+ {\setlayer
+ [page]
+ [preset=middlebottom,voffset=5mm]
+ {\showmathcharactersbodyfontschars{#2}}}}
+
+% dimensions
+
+\unexpanded\def\showmathcharactersentrywdhtdpic#1#2#3#4%
+ {\setlayer
+ [page]
+ [preset=leftbottom,offset=5mm]
+ {\somedata[align=normal,width=5cm]
+ {\strut width \hfill \the\dimexpr#1sp\par
+ \strut height\hfill \the\dimexpr#2sp\par
+ \strut depth \hfill \the\dimexpr#3sp\par
+ \strut italic\hfill \the\dimexpr#4sp}}}
+
+\unexpanded\def\showmathcharactersentryresource#1%
+ {} % {virtual: #1\par}
+
+% next
+
+\unexpanded\def\showmathcharactersstartnext
+ {\setlayer
+ [page]
+ [preset=middleleft,hoffset=5mm]
+ \bgroup\vbox\bgroup}
+
+\unexpanded\def\showmathcharactersstopnext
+ {\egroup\egroup}
+
+\unexpanded\def\showmathcharactersnextentry#1#2#3%
+ {\ifnum#1>1 \vskip1ex \fi
+ \dontleavehmode\somedata
+ [align=normal,width=4cm]
+ {\strut\showmathcharacterstxt{#2}\hfill\showmathcharacterschr{2}{#3}}\par}
+
+\unexpanded\def\showmathcharactersnextcycle#1%
+ {\ifnum#1>1 \vskip1ex \fi
+ \dontleavehmode\somedata
+ [align=normal,width=4cm]
+ {\strut\showmathcharacterstxt{cycle}}\par}
+
+% variants
+
+\unexpanded\def\showmathcharactersstartvvariants
+ {\setlayer
+ [page]
+ [preset=lefttop,offset=5mm]
+ \bgroup\vbox\bgroup}
+
+\unexpanded\def\showmathcharactersstarthvariants
+ {\setlayer
+ [page]
+ [preset=lefttop,offset=5mm]
+ \bgroup\hbox\bgroup}
+
+\unexpanded\def\showmathcharactersvvariantsentry#1#2#3%
+ {\ifnum#1>1 \vskip1ex \fi
+ \dontleavehmode\somedata
+ [align=middle,width=4cm]
+ {\strut\showmathcharacterstxt{#2}\hfilll\showmathcharacterschr{2}{#3}}}
+
+\unexpanded\def\showmathcharactershvariantsentry#1#2#3%
+ {\ifnum#1>1 \hskip1ex \else \dontleavehmode \fi
+ \somedata
+ [align={none,middle},height=2cm]
+ {\strut\showmathcharacterstxt{#2}\vfilll\showmathcharacterschr{2}{#3}}}
+
+\unexpanded\def\showmathcharactersstopvvariants
+ {\egroup\egroup}
+
+\unexpanded\def\showmathcharactersstophvariants
+ {\egroup\egroup}
+
+\unexpanded\def\showmathcharactersbetweennextandvariants
+ {}
+
+% classes
+
+\unexpanded\def\showmathcharactersstartentryclassspec
+ {\setlayer
+ [page]
+ [preset=rightbottom,offset=5mm]
+ \bgroup\somedata[align=normal,width=8cm]\bgroup}
+
+\unexpanded\def\showmathcharactersstopentryclassspec
+ {\egroup\egroup}
+
+\unexpanded\def\showmathcharactersentryclassname#1#2%
+ {\strut{\showmathcharacterstxt#1}\hfill\showmathcharacterstxt{#2}\par}
+
+% symbols
+
+\unexpanded\def\showmathcharactersentrysymbol#1#2%
+ {\setlayer
+ [page]
+ [preset=leftbottom,hoffset=5mm,voffset=50mm]
+ {\somedata
+ [align=normal,width=5cm]
+ {\showmathcharacterstxt{#1}\hfill\showmathcharacterschr{4}{#2}}}}
+
+% alternates
+
+\unexpanded\def\showmathcharactersstartlookupvariants
+ {\setlayer
+ [page]
+ [preset=middleright,hoffset=5mm]
+ \bgroup\vbox\bgroup}
+
+\unexpanded\def\showmathcharactersstoplookupvariants
+ {\egroup\egroup}
+
+\unexpanded\def\showmathcharacterslookupvariant#1#2#3#4%
+ {\ifnum#1>1 \vskip1ex \fi
+ \somedata
+ [align=normal,width=7cm]
+ {\showmathcharacterstxt{#4:} \showmathcharacterstxt{#2}\hfill\showmathcharacterschr{4}{#3}}}
+
+% main
+
+% this is a one-run style so we can forget about an alternative
+% just assume that the previous definitions are global
+
+\unprotect
+
+\unexpanded\def\showmathfontrepertoire
+ {\dosingleempty\module_math_repertoire_show}
+
+\def\module_math_repertoire_show[#1]%
+ {\showmathfontcharacters[alternative=,option=,check=yes,#1]
+ \showmathfontcharacters[alternative=,option=,#1]}
+
+\protect
+
+\stopmodule
+
+\continueifinputfile{s-math-repertoire.mkiv}
+
+\showmathcharacterssetbodyfonts{lucidaot,cambria,xits,modern,pagella,termes,bonum,schola,dejavu}
+
+\starttext
+
+ \doifelse {\getdocumentargument{bodyfont}} {} {
+
+ % \setupbodyfont[cambria, 12pt]
+ % \setupbodyfont[modern, 12pt]
+ % \setupbodyfont[lmvirtual, 12pt]
+ % \setupbodyfont[pxvirtual, 12pt]
+ % \setupbodyfont[txvirtual, 12pt]
+ % \setupbodyfont[palatino, 10pt]
+ % \setupbodyfont[mathtimes, 12pt]
+ % \setupbodyfont[stix, 12pt]
+ % \setupbodyfont[xits, 12pt]
+ % \setupbodyfont[lucida, 12pt]
+ % \setupbodyfont[lucidaot, 12pt]
+ % \setupbodyfont[pagella, 12pt]
+ % \setupbodyfont[bonum, 12pt]
+ % \setupbodyfont[schola, 12pt]
+ \setupbodyfont[dejavu, 12pt]
+
+ } {
+
+ \normalexpanded{\setupbodyfont[\getdocumentargument{bodyfont},12pt]}
+
+ }
+
+ \showmathfontrepertoire
+
+\stoptext
+
diff --git a/tex/context/modules/mkiv/s-mod-00.mkiv b/tex/context/modules/mkiv/s-mod-00.mkiv
new file mode 100644
index 000000000..7af56dc2d
--- /dev/null
+++ b/tex/context/modules/mkiv/s-mod-00.mkiv
@@ -0,0 +1,24 @@
+%D \module
+%D [ file=s-mod-00,
+%D version=very-old,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Documentation Base Environment,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+\startmode[nocode]
+
+ % \definieerbuffer[definition] % ignore
+
+ \def\startdefinition#1\stopdefinition{}
+
+\stopmode
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/s-mod-01.mkiv b/tex/context/modules/mkiv/s-mod-01.mkiv
new file mode 100644
index 000000000..6946bef69
--- /dev/null
+++ b/tex/context/modules/mkiv/s-mod-01.mkiv
@@ -0,0 +1,390 @@
+%D \module
+%D [ file=s-mod-01,
+%D version=very-old,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Module Documentation,
+%D author={Hans Hagen \& Luigi Scarso},
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+%D Usage:
+%D
+%D \starttyping
+%D context --ctx=s-mod somefile.mkiv
+%D \stoptyping
+%D
+%D In due time there will be a \type {mtx-context-module} as well.
+%D
+%D Luigi Scarso coordinates the processing of modules so best contact
+%D him when you run into problems. Bugs in this style can be sent to
+%D Hans.
+
+\usemodule
+ [abr-00]
+
+\setvariables
+ [document]
+ [ file=\jobname,
+ type=unknown,
+ version={\currentdate[\v!year,{.},\v!month,{.},\v!day]},
+ system=\CONTEXT,
+ title=Unknown Title,
+ subtitle=,
+ author=Unknown Author,
+ date=\currentdate,
+ copyright=Unknown Copyright,
+ suggestions=]
+
+\let\module\setupdocument
+
+\definepalet [module:unknown] [localone=black,localtwo=white]
+
+\definepalet [module:tex] [localone=blue,localtwo=green]
+\definepalet [module:mkii] [localone=blue,localtwo=green]
+\definepalet [module:mkiv] [localone=blue,localtwo=green]
+\definepalet [module:mkvi] [localone=blue,localtwo=green]
+
+\definepalet [module:lua] [localone=red,localtwo=green]
+\definepalet [module:cld] [localone=red,localtwo=green]
+
+\definepalet [module:mp] [localone=red,localtwo=blue]
+\definepalet [module:mpii] [localone=red,localtwo=blue]
+\definepalet [module:mpiv] [localone=red,localtwo=blue]
+\definepalet [module:metapost][localone=red,localtwo=blue]
+
+\setuppalet
+ [module:unknown]
+
+\startuseMPgraphic{page}
+
+ StartPage ;
+
+ color local_white ; local_white := .8white ;
+ color local_one ; local_one := \MPcolor{localone} randomized (.6,.8) ;
+ color local_two ; local_two := \MPcolor{localtwo} randomized (.3,.4) ;
+
+ color local_one ; local_one := .75[\MPcolor{localone},white] ;
+ color local_two ; local_two := .75[\MPcolor{localtwo},white] ;
+
+ numeric width ; width := bbwidth Page ;
+ numeric height ; height := bbheight Page ;
+
+ u := width/400 ;
+
+ def a_module (expr dx, dy) =
+ picture p ; p := image (
+ ddy := 0 ; sx := 60u ;
+ for i=1 upto (4 randomized 2) :
+ sy := 7u randomized 3u ;
+ fill unitsquare xyscaled(sx,sy) shifted (0,ddy) withcolor local_two ;
+ ddy := ddy + sy + 4u ;
+ endfor ;
+ ) ;
+ p := p shifted (dx,dy) shifted - center p ;
+ fill boundingbox p enlarged 8u withcolor local_white ;
+ fill boundingbox p enlarged 4u withcolor local_one ;
+ draw p ;
+ enddef ;
+
+ set_grid(width, height, width/15, height/15) ;
+
+ forever:
+ if new_on_grid(uniformdeviate width,uniformdeviate height):
+ a_module(dx,dy) ;
+ fi ;
+ exitif grid_full ;
+ endfor ;
+
+ picture p ;
+
+ draw image (
+ draw anchored.urt(textext("\bf\strut\documentvariable{system}") ysized 5.0cm,urcorner Page shifted (-1cm,- 1cm)) ;
+ draw anchored.urt(textext("\bf\strut\documentvariable{title}") ysized 1.5cm,urcorner Page shifted (-1cm,- 8cm)) ;
+ draw anchored.urt(textext("\bf\strut\documentvariable{subtitle}") ysized 1.5cm,urcorner Page shifted (-1cm,-10cm)) ;
+ draw anchored.urt(textext("\bf\strut\documentvariable{author}") ysized 1.5cm,lrcorner Page shifted (-1cm, 5cm)) ;
+ draw anchored.urt(textext("\bf\strut\currentdate") ysized 1.5cm,lrcorner Page shifted (-1cm, 3cm)) ;
+ ) withcolor .25white ;
+
+ StopPage ;
+
+\stopuseMPgraphic
+
+\startsetups[document:start]
+
+ \setuppalet
+ [module:\documentvariable{type}]
+
+ \startTEXpage
+ \useMPgraphic{page}
+ \stopTEXpage
+
+ \page[right]
+
+\stopsetups
+
+\startsetups[document:stop]
+
+ \page
+
+ \placeregister
+ [\v!index]
+ [\c!balance=\v!yes,
+ \c!indicator=\v!no,
+ \c!criterium=\v!text]
+
+\stopsetups
+
+% In order to be able to typeset this one too, we need to avoid
+% direct backslashed names.
+
+\starttexdefinition startmoduledocumentation
+ \starttext
+ \page
+ \begingroup
+ \startdocument
+\stoptexdefinition
+
+\starttexdefinition stopmoduledocumentation
+ \stopdocument
+ \page
+ \endgroup
+ \stoptext
+\stoptexdefinition
+
+\starttexdefinition startdocumentation
+ \par
+ \bgroup
+\stoptexdefinition
+
+\starttexdefinition stopdocumentation
+ \par
+ \egroup
+\stoptexdefinition
+
+\definetyping
+ [definition]
+
+\starttexdefinition startcompressdefinitions
+ \blank
+ \begingroup
+ \setuptyping[definition][bodyfont=small]
+\stoptexdefinition
+
+\starttexdefinition stopcompressdefinitions
+ \blank
+ \endgroup
+\stoptexdefinition
+
+\definetyping [PL] [\c!option=PL]
+\definetyping [JV] [\c!option=JV]
+\definetyping [MP] [\c!option=MP]
+\definetyping [TEX] [\c!option=TEX]
+\definetyping [LUA] [\c!option=LUA]
+
+\setuptyping [\v!typing] [\c!margin=\v!standard]
+\setuptyping [\v!file] [\c!margin=\v!standard]
+\setuptyping [definition] [\c!margin=0pt,\c!numbering=\v!line,\c!continue=\v!yes] % this continue wins
+
+\setuplinenumbering
+ [definition]
+ [\c!style=\ttx,
+ \c!distance=\leftmargindistance,
+ \c!align=\v!flushright]
+
+% This will be cleaned up.
+
+\unexpanded\def\domodulemarginstuff#1#2%
+ {\marginstuff
+ {\ifx#1\relax
+ \index{#2}%
+ \else
+ \index{#1{#2}}%
+ \fi
+ #1{\doboundtext{#2}{\leftmarginwidth}{..}}}}
+
+\unexpanded\def\modulemarginstuff#1#2% to be renamed
+ {\processcommalist[#2]{\domodulemarginstuff#1}}
+
+\definemargindata
+ [marginstuff]
+ [left]
+ [stack=yes,
+ hoffset=2em,
+ style=\ttxx]
+
+\unexpanded\def\macros {\modulemarginstuff\tex }
+\unexpanded\def\extras {\modulemarginstuff\relax}
+\unexpanded\def\elements{\modulemarginstuff\someelement}
+
+\unexpanded\def\someelement#1{\type{<#1>}}
+
+% [index]{command}
+
+% \macros{a,b}
+% \macros{a,b}{b}
+% \macros[a]{a,b}{b}
+
+% weg ermee (indeed):
+%
+% \defineparagraphs [interface] [\c!n=2]
+% \setupparagraphs [interface] [1] [\c!width=4cm]
+
+\starttexdefinition startexample
+ \par
+ \startnarrower
+\stoptexdefinition
+
+\starttexdefinition stopexample
+ \stopnarrower
+\stoptexdefinition
+
+%D Command references:
+
+\usemodule[int-load] \loadsetups
+
+\let\showsetup\setup
+
+\setupframedtexts
+ [setuptext]
+ [\c!background=\v!screen,
+ \c!frame=\v!off]
+
+% style (we use dejavu as it supports more characters)
+
+\switchtobodyfont
+ [dejavu-condensed,10pt] % preload
+
+\setupbodyfont
+ [dejavu,10pt] % main font
+
+\mainlanguage
+ [en]
+
+\setuptyping
+ [\v!typing]
+ [\c!bodyfont=dejavu-condensed]
+
+\setupwhitespace
+ [\v!big]
+
+\setuptolerance
+ [\v!verytolerant,\v!stretch]
+
+\setuplayout
+ [\c!backspace=3.5cm,
+ \c!leftmargin=1.75cm,
+ \c!rightmargin=0cm,
+ \c!margindistance=.5cm,
+ \c!leftedgedistance=.25cm,
+ \c!rightedgedistance=.5cm,
+ \c!edge=1.5cm,
+ \c!width=15.55333cm, % 13.998cm at 9pt => 15.55333 at 10pt
+ \c!topspace=2cm,
+ \c!header=1.25cm,
+ \c!footer=1.25cm,
+ \c!height=middle,
+ \c!style=\ss]
+
+\setuppagenumbering
+ [\c!location=]
+
+\setuppagenumbering
+ [\c!alternative={\v!doublesided,\v!singlesided}]
+
+\setupfootertexts
+ [\v!edge]
+ [][\pagenumber]
+
+\setupfootertexts
+ [\v!margin]
+ [\filename{\documentvariable{file}}][]
+ [\filename{\documentvariable{file}}][]
+
+\setupfootertexts
+ [\v!text]
+ [\CONTEXT]
+ [\documentvariable{title}]
+
+\setupheadertexts
+ [\v!text]
+ []
+ [\documentvariable{subtitle}]
+
+\setupinmargin
+ [\c!location=\v!left]
+
+\setupheads
+ [\c!alternative=\v!inmargin]
+
+\setuphead
+ [\v!chapter]
+ [\c!style=\bfc,
+ \c!page=\v!right,
+ \c!header=\v!empty]
+
+\setuphead
+ [\v!section]
+ [\c!style=\bfb,
+ \c!page=\v!right]
+
+\setuphead
+ [\v!subsection]
+ [\c!style=\bfa]
+
+\setuplist
+ [\v!chapter]
+ [\c!style=\v!bold,
+ \c!after=\blank]
+
+\setupcombinedlist
+ [\v!content]
+ [\c!width=3em,
+ \c!aligntitle=\v!yes]
+
+\setupregister
+ [\v!index]
+ [\c!balance=\v!yes,
+ \c!indicator=\v!no]
+
+\setupinteraction
+ [\c!state=\v!start,
+ \c!color=,
+ \c!contrastcolor=,
+ \c!style=]
+
+% modes
+
+\doifmode {nocolor} {
+
+ \setupcolors
+ [\c!conversion=\v!always]
+
+}
+
+\doifmode {singlesided} {
+
+ \setuppagenumbering
+ [\c!alternative=\v!singlesided]
+
+ \setupfootertexts
+ [\v!margin]
+ [\filename{\documentvariable{file}}][]
+
+}
+
+% bonus
+
+\usemodule
+ [abr-02]
+
+% another one
+
+\dontcomplain
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/s-mod-02.mkiv b/tex/context/modules/mkiv/s-mod-02.mkiv
new file mode 100644
index 000000000..37e3d2f14
--- /dev/null
+++ b/tex/context/modules/mkiv/s-mod-02.mkiv
@@ -0,0 +1,24 @@
+%D \module
+%D [ file=s-mod-02,
+%D version=very-old,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Documentation Screen Environment,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+\startmode[nocode]
+
+ % \definieerbuffer[definition] % ignore
+
+ \def\startdefinition#1\stopdefinition{}
+
+\stopmode
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/s-pages-statistics.mkiv b/tex/context/modules/mkiv/s-pages-statistics.mkiv
new file mode 100644
index 000000000..375dd9949
--- /dev/null
+++ b/tex/context/modules/mkiv/s-pages-statistics.mkiv
@@ -0,0 +1,134 @@
+%D \module
+%D [ file=s-pages-statistics, % s-otr-01,
+%D version=2012.02.02,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Page Statistics,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+\defineframed
+ [otrstatistics]
+ [\c!offset=\bodyfontsize,
+ \c!background=\v!color,
+ \c!strut=\v!no,
+ \c!backgroundcolor=white,
+ \c!frame=\v!off,
+ \c!align=\v!middle]
+
+\definecombination
+ [otrstatistics]
+ [\c!nx=2,
+ \c!ny=2,
+ \c!location=\v!top]
+
+% expansion is neeed because tabulate sets some penalties
+
+\startsetups system:page:otr:statistics
+ \normalexpanded {
+ \startcombination[otrstatistics] {
+ \otrstatistics {
+ \starttabulate[|Tw(10em)|Trw(8em)|]
+ \NC \string\textwidth \NC \the\textwidth \NC \NR
+ \NC \string\textheight \NC \the\textheight \NC \NR
+ \NC \string\lineheight \NC \the\lineheight \NC \NR
+ \NC \string\strutheight \NC \strutheight \NC \NR
+ \NC \string\strutdepth \NC \strutdepth \NC \NR
+ % \NC \string\vsize \NC \the\vsize \NC \NR
+ \NC \string\topskip \NC \the\topskip \NC \NR
+ \stoptabulate
+ }
+ } {}
+ {
+ \otrstatistics {
+ \starttabulate[|Tw(10em)|Trw(8em)|]
+ \NC \string\pagegoal \NC \the\pagegoal \NC \NR
+ \NC \string\pagetotal \NC \the\pagetotal \NC \NR
+ \NC \string\pagedepth \NC \the\pagedepth \NC \NR
+ \NC \string\pageshrink \NC \the\pageshrink \NC \NR
+ \NC \string\pagestretch \NC \the\pagestretch \NC \NR
+ \NC \string\pagefilstretch \NC \the\pagefilstretch \NC \NR
+ \NC \string\pagefillstretch \NC \the\pagefillstretch \NC \NR
+ \NC \string\pagefilllstretch \NC \the\pagefilllstretch \NC \NR
+ \stoptabulate
+ }
+ } {}
+ {
+ \otrstatistics {
+ \starttabulate[|Tw(10em)|Trw(8em)|]
+ \NC \string\widowpenalty \NC \the\widowpenalty \NC \NR
+ \NC \string\clubpenalty \NC \the\clubpenalty \NC \NR
+ \NC \string\displaywidowpenalty \NC \the\displaywidowpenalty \NC \NR
+ \NC \string\brokenpenalty \NC \the\brokenpenalty \NC \NR
+ \NC \string\interlinepenalty \NC \the\interlinepenalty \NC \NR
+ \stoptabulate
+ }
+ } {}
+ {
+ \otrstatistics[\c!foregroundcolor=darkred] {
+ \starttabulate[|Tw(10em)|Trw(8em)|]
+ \NC page \NC \the\realpageno \NC \NR
+ \NC delta \NC \the\dimexpr\pagegoal-\pagetotal\relax \NC \NR
+ \stoptabulate
+ }
+ } {}
+ \stopcombination
+ }
+\stopsetups
+
+\defineoverlay
+ [system:page:otr:statistics]
+ [{\framed
+ [\c!offset=\bodyfontsize,
+ \c!background=\v!color,
+ \c!strut=\v!no,
+ %\c!frame=\v!off,
+ \c!framecolor=white,
+ \c!frameoffset=\onepoint,
+ \c!rulethickness=2\onepoint,
+ \c!backgroundcolor=darkblue,
+ \c!align=\v!middle]
+ {\forgetall
+ \insidefloattrue
+ \setups{system:page:otr:statistics}}}]
+
+\unexpanded\def\page_one_command_package_show_state_indeed
+ {\scratchheight\ht\b_page_one_contents
+ \scratchdepth \dp\b_page_one_contents
+ \setbox\b_page_one_contents\vbox\framed
+ [\c!offset=\v!overlay,
+ \c!framecolor=darkred,
+ \c!rulethickness=\onepoint,
+ \c!background={\v!foreground,system:page:otr:statistics}]
+ {\lower\scratchdepth\box\b_page_one_contents}%
+ \ht\b_page_one_contents\scratchheight
+ \dp\b_page_one_contents\scratchdepth}
+
+\let\page_one_command_package_show_state\relax
+
+\unexpanded\def\showpageproperties
+ {\let\page_one_command_package_show_state\page_one_command_package_show_state_indeed}
+
+\protect
+
+\continueifinputfile{s-pages-statistics.mkiv}
+
+\setupbodyfont[dejavu,11pt] \dontcomplain \showpageproperties
+
+\starttext
+
+\dorecurse {100} { \input tufte
+
+ \startitemize[packed,intro]
+ \startitem test \stopitem \startitem test \stopitem
+ \startitem test \stopitem \startitem test \stopitem
+ \stopitemize
+}
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-physics-units.mkiv b/tex/context/modules/mkiv/s-physics-units.mkiv
new file mode 100644
index 000000000..50adc8014
--- /dev/null
+++ b/tex/context/modules/mkiv/s-physics-units.mkiv
@@ -0,0 +1,30 @@
+%D \module
+%D [ file=s-physics-units, % s-phy-01,
+%D version=2011-11-24,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Physics Units,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[physics-units]
+
+\registerctxluafile{s-physics-units}{}
+
+\installmodulecommandluasingle \showunits {moduledata.physics.units.showlist}
+
+\stopmodule
+
+\continueifinputfile{s-physics-units.mkiv}
+
+\usemodule[art-01]
+
+\starttext
+
+ \showunits
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-pre-17.mkiv b/tex/context/modules/mkiv/s-pre-17.mkiv
new file mode 100644
index 000000000..9c46b4ed7
--- /dev/null
+++ b/tex/context/modules/mkiv/s-pre-17.mkiv
@@ -0,0 +1,408 @@
+%D \module
+%D [ file=s-pre-17,
+%D version=1999.08.20,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Presentation Environment 17,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\setupbodyfont
+ [dejavu,11pt,ss]
+
+\setupsystem
+ [random=medium]
+
+\setupbackgrounds
+ [state=repeat]
+
+\setupbackgrounds
+ [page]
+ [background={page,forward}]
+
+\setupbackgrounds
+ [text][text]
+ [background=blowdown]
+
+\setupinteraction
+ [state=start,
+% click=off,
+ color=TitleColor,
+ contrastcolor=TitleColor]
+
+\setupinteractionscreen
+ [option=max]
+
+\setuptolerance
+ [verytolerant,stretch]
+
+\definecolor [BackColor] [s=.30]
+\definecolor [PageColor] [g=1]
+\definecolor [FrameColor] [r=1]
+\definecolor [TitleColor] [y=1]
+\definecolor [ArrowColor] [b=1]
+
+\defineoverlay [title] [\useMPgraphic{title}]
+\defineoverlay [page] [\useMPgraphic{page}]
+\defineoverlay [blowup] [{\hboxreference[whatever:\CurrentBlaBla]{\overlaybutton{whatever:\CurrentBlaBla}}}]
+\defineoverlay [blowdown] [{\hboxreference[page:\realfolio]{\overlaybutton{page:\realfolio}}}]
+\defineoverlay [forward] [\overlaybutton{forward}]
+
+\startMPinclusions
+ input "mp-abck.mpiv" ;
+\stopMPinclusions
+
+\startMPpositiongraphic{mppos:connection}
+ path pa, pb, pc ; pair ca, cb ;
+ initialize_box(\MPpos{\MPvar{self}}) ; pa := pxy ; ca := cxy ;
+ initialize_box(\MPpos{\MPvar{prev}}) ; pb := pxy ; cb := cxy ;
+ pickup pencircle scaled .5pt ;
+ pa := pa enlarged 10pt ;
+ pb := pb enlarged 10pt ;
+ for i=1 upto 10 :
+ draw pa randomized 20pt withcolor \MPcolor{FrameColor} ;
+ endfor ;
+ if \MPp{\MPvar{prev}} > 0 :
+ pair a, b, c, d ;
+ for i=1 upto 25 :
+ a := .5[ulcorner pa,urcorner pa] randomized (10pt,10pt) ;
+ b := .5[llcorner pb,lrcorner pb] randomized (20pt,10pt) ;
+ c := .5[a,b] rotatedaround(a,-25) randomized (5pt,5pt) ;
+ d := .5[a,b] rotatedaround(a,+25) randomized (5pt,5pt) ;
+ draw (a--b) withcolor \MPcolor{ArrowColor} ;
+ draw (d--a--c) withcolor \MPcolor{ArrowColor} ;
+ endfor ;
+ fi ;
+ anchor_box(\MPanchor{\MPvar{self}}) ;
+\stopMPpositiongraphic
+
+\startuseMPgraphic{title}
+ pickup pencircle scaled .5pt ;
+ for i=1 upto 10 :
+ draw fullsquare
+ xyscaled(OverlayWidth,OverlayHeight)
+ enlarged 10pt randomized 20pt
+ withcolor \MPcolor{TitleColor} ;
+ endfor ;
+\stopuseMPgraphic
+
+\startuseMPgraphic{page}
+ StartPage ;
+ pickup pencircle scaled .5pt ;
+ fill Page withcolor \MPcolor{BackColor} ;
+ for i=1 upto 20 :
+ draw Page enlarged -50pt randomized 50pt withcolor \MPcolor{PageColor} ;
+ endfor ;
+ StopPage ;
+\stopuseMPgraphic
+
+\newcounter\CurrentBlaBla
+
+\defineframedtext
+ [ZoomText]
+ [before=,
+ after=,
+ width=fit,
+ align=flushleft,
+ frame=off,
+ foregroundcolor=white]
+
+\starttexdefinition StartText
+ \begingroup
+ \getrandomdimen\scratchdimen{250pt}{350pt}
+ \edef\TextWidth{\the\scratchdimen}
+ \setbox\scratchbox\hbox\bgroup
+ \hsize\TextWidth
+ \ZoomText\bgroup
+\stoptexdefinition
+
+\starttexdefinition StartTextSimple
+ \begingroup
+ \setbox\scratchbox\hbox\bgroup
+ \framed [
+ frame=off,
+ foregroundcolor=white
+ ]
+ \bgroup
+\stoptexdefinition
+
+\starttexdefinition StopTextSimple
+ \StopText
+\stoptexdefinition
+
+\starttexdefinition StopText
+ \egroup
+ \egroup
+ \doglobal\increment\CurrentBlaBla
+ \let\PrevBlaBla\CurrentBlaBla \doglobal\decrement\PrevBlaBla
+ \let\NextBlaBla\CurrentBlaBla \doglobal\increment\NextBlaBla
+ \setMPpositiongraphic {
+ connection:\CurrentBlaBla
+ }{
+ mppos:connection
+ }{
+ seed=\CurrentBlaBla,
+ prev=connection:\PrevBlaBla,
+ next=connection:\NextBlaBla
+ }
+ \hbox to 600pt \bgroup
+ \setupinteraction[focus=fitr]
+ \getrandomdimen\scratchdimen{50pt}{75pt}
+ \hskip0pt plus \scratchdimen minus \scratchdimen
+ \hpos {
+ connection:\CurrentBlaBla
+ }{
+ \framed [
+ frame=off,
+ offset=overlay,
+ backgroundoffset=50pt,
+ background=blowup
+ ] {
+ \box\scratchbox
+ }
+ }
+ \getrandomdimen\scratchdimen{50pt}{75pt}
+ \hskip0pt plus \scratchdimen minus \scratchdimen
+ \egroup
+ \getrandomdimen\scratchdimen{75pt}{125pt}
+ \vskip\scratchdimen
+ \endgroup
+\stoptexdefinition
+
+\dontcomplain
+
+\disablemode[SpreadPage]
+
+\starttexdefinition StartPage #1
+ \doglobal\increment(\CurrentBlaBla,100)%
+ \setvariables[StartPage][title={#1}]
+ \setbox\scratchbox\hbox\bgroup
+ \valign\bgroup
+ \ignorespaces
+ \alignmark\alignmark
+ \vss
+ \cr
+\stoptexdefinition
+
+\starttexdefinition StopPage
+ \cr
+ \egroup
+ \egroup
+ \setbox\scratchbox\vbox\bgroup
+ \vskip100pt
+ \doifelsemode {SpreadPage} {
+ \hbox spread 200pt
+ } {
+ \hbox to \wd\scratchbox
+ }
+ \bgroup
+ \setupinteraction[focus=fitr]
+ \hss
+ \switchtobodyfont[big]%
+ \bfd
+ \doglobal\increment\CurrentBlaBla
+ \setupinterlinespace
+ \hpos {
+ title:\realfolio
+ }{
+ \framed [
+ frame=off,
+ offset=overlay,
+ backgroundoffset=50pt,
+ background=blowup
+ ] {
+ \framed [
+ frame=off,
+ align=middle,
+ foregroundcolor=white,
+ background=title
+ ] {
+ \getvariable{StartPage}{title}
+ }
+ }
+ }
+ \hss
+ \egroup
+ \vskip100pt
+ \box\scratchbox
+ \egroup
+ \normalexpanded {
+ \definepapersize [
+ MasterPage
+ ] [
+ width=\the\dimexpr\wd\scratchbox+100pt\relax,
+ height=\the\dimexpr\ht\scratchbox+\dp\scratchbox+100pt\relax
+ ]
+ }
+ \setuppapersize
+ [MasterPage]
+ [MasterPage]
+ \setuplayout [
+ topspace=25pt,
+ backspace=25pt,
+ width=middle,
+ header=0pt,
+ footer=0pt,
+ height=middle
+ ]
+ \centerbox {
+ \box\scratchbox
+ }
+ \page
+\stoptexdefinition
+
+\starttexdefinition StartTopic #1
+ \removeunwantedspaces
+ \cr
+ \doglobal\increment(\CurrentBlaBla,100)%
+ \vbox\bgroup
+ \vskip100pt
+ \bgroup
+ \definecolor[FrameColor][TitleColor]
+ \switchtobodyfont[big]
+ \bfd
+ \setupinterlinespace
+ \StartTextSimple
+ \ignorespaces
+ #1
+ \removeunwantedspaces
+ \StopTextSimple
+ \vskip25pt
+ \egroup
+\stoptexdefinition
+
+\starttexdefinition StopTopic
+ \vskip-\lastskip
+ \vskip100pt
+ \egroup
+ \ignorespaces
+\stoptexdefinition
+
+\starttexdefinition TitlePage #1
+ \begingroup
+ \def\\{\vskip1ex\bfc\def\\{\vskip1ex\bfb}}
+ \enablemode[SpreadPage]
+ \StartPage{#1}
+ \StopPage
+ \endgroup
+\stoptexdefinition
+
+\continueifinputfile{s-pre-17.mkiv}
+
+\starttext
+
+\nopdfcompression
+
+\TitlePage{About Text\\Today's Talk\\Hans Hagen}
+
+\StartPage{The First Page}
+
+ \StartTopic{Some Text}
+ \StartText \input tufte \StopText
+ \StartText \input knuth \StopText
+ \StartText \input zapf \StopText
+ \StartText \input knuth \StopText
+ \StopTopic
+
+ \StartTopic{Another Text}
+ \StartText \input tufte \StopText
+ \StartText \input zapf \StopText
+ \StartText \input knuth \StopText
+ \StopTopic
+
+ \StartTopic{Some More Text}
+ \StartText \input tufte \StopText
+ \StartText \input knuth \StopText
+ \StartText \input zapf \StopText
+ \StartText \input tufte \StopText
+ \StopTopic
+
+ \StartTopic{Some Text Again}
+ \StartText \input tufte \StopText
+ \StartText \input zapf \StopText
+ \StartText \input knuth \StopText
+ \StartText \input tufte \StopText
+ \StopTopic
+
+\StopPage
+
+\StartPage{The Second Page}
+
+ \StartTopic{Some Text}
+ \StartText \input tufte \StopText
+ \StartText \input knuth \StopText
+ \StartText \input zapf \StopText
+ \StartText \input tufte \StopText
+ \StartText \input knuth \StopText
+ \StartText \input zapf \StopText
+ \StopTopic
+
+ \StartTopic{Another Text}
+ \StartText \input tufte \StopText
+ \StartText \input knuth \StopText
+ \StartText \input zapf \StopText
+ \StopTopic
+
+ \StartTopic{Some Nice Text}
+ \StartText \input tufte \StopText
+ \StartText \input knuth \StopText
+ \StartText \input zapf \StopText
+ \StartText \input tufte \StopText
+ \StartText \input tufte \StopText
+ \StopTopic
+
+ \StartTopic{Some Funny Text}
+ \StartText \input tufte \StopText
+ \StartText \input knuth \StopText
+ \StopTopic
+
+ \StartTopic{Quite Some Text}
+ \StartText \input tufte \StopText
+ \StartText \input knuth \StopText
+ \StartText \input zapf \StopText
+ \StartText \input tufte \StopText
+ \StartText \input knuth \StopText
+ \StartText \input zapf \StopText
+ \StartText \input tufte \StopText
+ \StartText \input knuth \StopText
+ \StartText \input zapf \StopText
+ \StopTopic
+
+ \StartTopic{Even More Text}
+ \StartText \input knuth \StopText
+ \StartText \input zapf \StopText
+ \StartText \input tufte \StopText
+ \StartText \input knuth \StopText
+ \StartText \input zapf \StopText
+ \StopTopic
+
+\StopPage
+
+\StartPage{The Third Page}
+
+ \StartTopic{Some Short Text}
+ \StartText \input tufte \StopText
+ \StartText \input knuth \StopText
+ \StartText \input zapf \StopText
+ \StopTopic
+
+ \StartTopic{Some Minimal Text}
+ \StartText \input tufte \StopText
+ \StartText \input zapf \StopText
+ \StopTopic
+
+ \StartTopic{Some More Text}
+ \StartText \input tufte \StopText
+ \StartText \input knuth \StopText
+ \StartText \input zapf \StopText
+ \StartText \input tufte \StopText
+ \StartText \input tufte \StopText
+ \StopTopic
+
+\StopPage
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-pre-30.mkiv b/tex/context/modules/mkiv/s-pre-30.mkiv
new file mode 100644
index 000000000..1be85d02b
--- /dev/null
+++ b/tex/context/modules/mkiv/s-pre-30.mkiv
@@ -0,0 +1,257 @@
+%D \module
+%D [ file=s-pre-30,
+%D version=2006.04.25,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Presentation Environment 30,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\usemodule[streams]
+
+\setuppapersize
+ [S6] [S6]
+
+\setupbodyfont
+ [12pt,tt]
+
+\definemeasure[bleed][6pt]
+
+\definecolor[maincolor-3][r=.5,g=.2,b=.2]
+\definecolor[maincolor-1][r=.2,g=.5,b=.2]
+\definecolor[maincolor-2][r=.2,g=.2,b=.5]
+
+\definecolor[pagecolor] [s=.5]
+\definecolor[resultcolor][s=1,t=.85,a=1]
+\definecolor[maincolor] [maincolor-3]
+
+\definecolorgroup
+ [maingroup]
+ [.5:.2:.2,
+ .2:.5:.2,
+ .2:.2:.5]
+
+\definecolorgroup
+ [resultgroup]
+ [1:.85:.85,
+ .85:1:.85,
+ .85:.85:1]
+
+\def\CurrentColor{1}
+
+\definecolor[maincolor] [maingroup:\CurrentColor]
+\definecolor[resultcolor][resultgroup:\CurrentColor]
+
+\def\NextColor{\ifnum\CurrentColor=3 \def\CurrentColor{1}\else\doglobal\increment\CurrentColor\fi}
+
+\setuplayout
+ [backspace=\measure{bleed},
+ topspace=30pt,
+ bottomdistance=\measure{bleed},
+ bottom=\dimexpr30pt-\measure{bleed}\relax,
+ header=0pt,
+ footer=0pt,
+ topdistance=\measure{bleed},
+ top=\dimexpr30pt-\measure{bleed}\relax,
+ width=middle,
+ height=middle]
+
+\setuptop
+ [before=\vfill,
+ after=\vfill,
+ strut=yes]
+
+\setupbackgrounds
+ [text]
+ [backgroundoffset=\measure{bleed},
+ background=color,
+ backgroundcolor=maincolor]
+
+\setupbackgrounds
+ [page]
+ [background=color,
+ backgroundcolor=pagecolor]
+
+\setupcolors
+ [state=start,
+ textcolor=white]
+
+\setuphead
+ [chapter]
+ [style=\ttd]
+
+\setuphead
+ [section]
+ [style=\ttd]
+
+\defineviewerlayer
+ [result]
+ [state=stop]
+
+\definelayer
+ [result]
+
+\setuplayer
+ [result]
+ [width=\textwidth,
+ height=\textheight]
+
+\setupbackgrounds
+ [text]
+ [background={color,toggle,foreground,result}]
+
+\setupinteraction
+ [state=start,
+ click=no,
+ menu=on,
+ color=white,
+ contrastcolor=white,
+ closepageaction=HideLayer{result},
+ openpageaction=HideLayer{result}]
+
+% We add a dummy color switch so that each page has at least one
+% transparency; else acrobat will render the pages differently
+% bug in 6 and 7).
+
+\startinteractionmenu[bottom]
+% \color[resultcolor]{ }%
+ \hfill
+ \got [content] content \\
+ \got [previouspage] previous \\
+ \got [nextpage] next \\
+ \got [ToggleLayer{result}] result \\
+ \got [CloseDocument] close \\
+\stopinteractionmenu
+
+\setuphead
+ [section]
+ [placehead=no,
+ incrementnumber=list]
+
+\setuplist
+ [section]
+ [alternative=f]
+
+\defineoverlay
+ [toggle]
+ [\overlaybutton{ToggleLayer{result}}]
+
+\definestreamlayer
+ [resultstream]
+
+\startsetups streamlayer:resultstream:flush
+ \setlayer
+ [result]
+ [preset=middlebottom,
+ hoffset=\measure{bleed},
+% voffset=-\measure{bleed}]
+ voffset=\measure{bleed}]
+ {\startviewerlayer[result]%
+% \framed
+% [offset=overlay,
+% frame=off,
+% background=color,
+% backgroundoffset=1\dimexpr\measure{bleed}\relax,
+% backgroundcolor=maincolor]
+ {\framed
+ [offset=overlay,
+ frame=off,
+ foregroundcolor=maincolor,
+ background=color,
+ backgroundoffset=1\dimexpr\measure{bleed}\relax,
+ backgroundcolor=resultcolor]
+ {\tightlayer[\currentstreamlayer]}}
+ \stopviewerlayer}
+\stopsetups
+
+\startsetups show-definition
+ \subject{definition}
+ \typebuffer
+ \getbuffer
+ \page
+\stopsetups
+
+\startsetups show-result
+ \startstreamlayer[resultstream]
+ \subject{result}
+ \getbuffer
+ \stopstreamlayer
+ \placestreamlayer[resultstream]
+ \page
+\stopsetups
+
+\startsetups show-usage
+ \subject{usage}
+ \typebuffer
+ \startstreamlayer[resultstream]
+ \subject{result}
+ \getbuffer
+ \stopstreamlayer
+ \placestreamlayer[resultstream]
+ \page
+\stopsetups
+
+\startsetups show-usage-lines
+ \subject{usage}
+ \typebuffer
+ \startstreamlayer[resultstream]
+ \subject{result}
+ \startlines
+ \getbuffer
+ \stoplines
+ \stopstreamlayer
+ \placestreamlayer[resultstream]
+ \page
+\stopsetups
+
+\startsetups show-contents
+ \startcolumns[n=3]
+ \placelist[section][criterium=text]
+ \stopcolumns
+ \pagereference[content]
+ \page
+ \NextColor
+\stopsetups
+
+\def\TitlePage#1%
+ {\title{#1}
+ \setups{show-contents}}
+
+\def\StartSample#1%
+ {\starttext
+ \page
+ \section{#1}
+ \setuptoptexts[][\processedfile\enspace:\enspace\lowercase{#1}]}
+
+\def\StopSample
+ {\page
+ \setuptoptexts[][]
+ \stoptext
+ \NextColor}
+
+\endinput
+
+\usemodule[pre-30]
+
+% \TitlePage{Howling to the moon}
+
+\StartSample{Basics}
+
+\startbuffer
+\lua{a = 1.5 ; b = 1.8 ; c = a*b ; context(c) ;}
+
+\startlua
+ a = 1
+ b = 2
+ c = a*b
+ context(c)
+\stoplua
+\stopbuffer
+
+\setups{show-usage}
+
+\StopSample
diff --git a/tex/context/modules/mkiv/s-pre-60.mkiv b/tex/context/modules/mkiv/s-pre-60.mkiv
new file mode 100644
index 000000000..70f8978b0
--- /dev/null
+++ b/tex/context/modules/mkiv/s-pre-60.mkiv
@@ -0,0 +1,212 @@
+%D \module
+%D [ file=s-pre-60,
+%D version=2004.03.15,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Presentation Environment 60,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% use lua instead of global mess
+
+\unprotect
+
+\startmode[paper,print]
+ \let\StartSteps\relax
+ \let\StopSteps \relax
+ \let\FlushStep \relax
+ \let\ResetStep \relax
+ \let\StartBusy \relax
+ \let\StopBusy \relax
+ \endinput
+\stopmode
+
+\newcount\c_module_pre_steps_current
+\newcount\c_module_pre_steps_maximum
+\newcount\c_module_pre_steps_nesting_step
+\newcount\c_module_pre_steps_nesting_steps
+\newcount\c_module_pre_steps_nesting_busy
+
+\def\StepCounter {\the\c_module_pre_steps_current}
+\def\StepMaximum {\the\c_module_pre_steps_maximum}
+\def\StepLayer {step:\the\c_module_pre_steps_current}
+\def\NextStepLayer {step:\the\numexpr\c_module_pre_steps_current+\plusone\relax}
+\def\FirstStepLayer{step:1}
+
+\useJSscripts[stp]
+
+\startsetups[set-stepper]
+
+ \ifnum\getvariable{stepper}{nofsteps}>\c_module_pre_steps_maximum
+
+ \dostepwiserecurse {\numexpr\c_module_pre_steps_maximum+\plusone\relax} {\getvariable{stepper}{nofsteps}} {1} {
+ \doifnotmode{nosteps,nostep} {
+ \expanded{\defineviewerlayer[step:\recurselevel][state=stop,scope=global]}
+ }
+ }
+
+ \global\c_module_pre_steps_maximum\getvariable{stepper}{nofsteps}\relax
+
+ \fi
+
+\stopsetups
+
+\setvariables
+ [stepper]
+ [set=\setups{set-stepper},
+ nofsteps=50]
+
+\defineviewerlayer[step:busy][state=start]
+
+\definereference [SetupStepper] [JS(SetupStepper{step,\StepMaximum})]
+\definereference [ResetStepper] [JS(ResetStepper)]
+\definereference [CheckStepper] [JS(CheckStepper{\StepCounter})]
+\definereference [InvokeStepper] [JS(InvokeStepper)]
+
+% todo: roll back blank
+
+\unexpanded\def\ResetStep
+ {\iftrialtypesetting\else
+ \global\c_module_pre_steps_current\zerocount
+ \fi}
+
+\unexpanded\def\NextStep
+ {\iftrialtypesetting\else
+ \global\advance\c_module_pre_steps_current\plusone
+ \fi}
+
+\unexpanded\def\PrevStep
+ {\iftrialtypesetting\else
+ \global\advance\c_module_pre_steps_current\minusone
+ \fi}
+
+\unexpanded\def\FlushStep
+ {\iftrialtypesetting\else
+ \StopStep
+ \NextStep
+ \StartStep
+ \fi}
+
+\unexpanded\def\StartStep
+ {\iftrialtypesetting\else
+ \global\advance\c_module_pre_steps_nesting_step\plusone
+ \ifcase\c_module_pre_steps_nesting_step\or
+ \startviewerlayer[\StepLayer]%
+ \fi
+ \fi
+ \ignorespaces}
+
+\unexpanded\def\StopStep
+ {\removeunwantedspaces
+ \iftrialtypesetting\else
+ \ifcase\c_module_pre_steps_nesting_step\or
+ \stopviewerlayer
+ \fi
+ \global\advance\c_module_pre_steps_nesting_step\minusone
+ \fi}
+
+\unexpanded\def\StartSteps
+ {\iftrialtypesetting\else
+ \global\advance\c_module_pre_steps_nesting_steps\plusone
+ \ifcase\c_module_pre_steps_nesting_steps\or
+ \ResetStep
+ \NextStep
+ \StartStep
+ \fi
+ \fi}
+
+\unexpanded\def\StopSteps
+ {\iftrialtypesetting\else
+ \ifcase\c_module_pre_steps_nesting_steps\or
+ \StopStep
+ \PrevStep
+ \fi
+ \global\advance\c_module_pre_steps_nesting_steps\minusone
+ \fi}
+
+\unexpanded\def\StartBusy
+ {\iftrialtypesetting\else
+ \global\advance\c_module_pre_steps_nesting_busy\plusone
+ \ifcase\c_module_pre_steps_nesting_busy\or
+ \startviewerlayer[step:busy]
+ \fi
+ \fi
+ \ignorespaces}
+
+\unexpanded\def\StopBusy
+ {\removeunwantedspaces
+ \iftrialtypesetting\else
+ \ifcase\c_module_pre_steps_nesting_busy\or
+ \stopviewerlayer
+ \fi
+ \global\advance\c_module_pre_steps_nesting_busy\minusone
+ \fi}
+
+%D Handy:
+
+\unexpanded\def\StartLocalSteps
+ {\ResetStep}
+
+\unexpanded\def\StopLocalSteps
+ {}
+
+\unexpanded\def\StartLocalStep
+ {\NextStep
+ \StartStep}
+
+\unexpanded\def\StopLocalStep
+ {\StopStep}
+
+\appendtoks
+ \ResetStep
+\to \everyaftershipout
+
+\setupinteraction
+ [%openaction=SetupStepper,
+ closeaction=ResetStepper,
+ openpageaction=CheckStepper,
+ closepageaction=ResetStepper]
+
+\defineoverlay[invoke][\overlaybutton{InvokeStepper}]
+
+\setupbackgrounds
+ [text]
+ [background=invoke]
+
+% bonus
+
+\useMPlibrary[nav]
+
+\definepalet
+ [navplus]
+ [attach=interactioncolor,
+ comment=interactioncolor]
+
+\setupcomment
+ [symbol={comment-normal,comment-down},
+ textlayer=\StepLayer,
+ option=buffer,
+ height=\textheight,
+ width=\textwidth,
+ margin=0pt]
+
+\setupattachments
+ [symbol={attach-normal,attach-down},
+ textlayer=\StepLayer]
+
+%D used as (given some definitions):
+%D
+%D \starttyping
+%D \StartLocalSteps
+%D \startcombination[both]
+%D {\StartLocalStep\placestreamlayer[left]\StopLocalStep} {}
+%D {\StartLocalStep\placestreamlayer[right]\StopLocalStep} {}
+%D \stopcombination
+%D \StopLocalSteps
+%D \stoptyping
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/s-pre-69.mkiv b/tex/context/modules/mkiv/s-pre-69.mkiv
new file mode 100644
index 000000000..cc6af9ad3
--- /dev/null
+++ b/tex/context/modules/mkiv/s-pre-69.mkiv
@@ -0,0 +1,336 @@
+%D \module
+%D [ file=s-pre-69,
+%D version=2010.04.28,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Presentation Environment 69,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\setuppapersize[S6][S6]
+\setuppapersize[SM][SM]
+
+\usemodule
+ [abr-01,pre-60]
+
+\setupinteraction
+ [state=start,
+ contrastcolor=white,
+ color=white,
+ click=no]
+
+\setuplayout
+ [location=middle,
+ topspace=60pt,
+ bottomspace=80pt,
+ backspace=80pt,
+ header=0pt,
+ footer=0pt,
+ width=middle,
+ height=middle]
+
+\setupcolors
+ [textcolor=white]
+
+\setupbodyfont[euler]
+
+\definecolor[maincolor] [blue]
+\definecolor[extracolor][green]
+
+% \definecolor[maincolor] [red]
+% \definecolor[extracolor][blue]
+
+\startMPinitializations
+ if unknown MyColor[1] :
+ color MyColor[] ;
+ MyColor[1] := transparent(1,.25,\MPcolor{maincolor}) ;
+ MyColor[2] := transparent(1,.25,\MPcolor{extracolor}) ;
+
+ picture MySoFar ; MySoFar := nullpicture ;
+ path MyLastOne ; MyLastOne := origin -- cycle ;
+ color MyPageColor ; MyPageColor := MyColor[1] ;
+ path MyLeftSteps, MyRightSteps ;
+ boolean MyPageDone ; MyPageDone := false ;
+
+ vardef MySmallShape(expr parent) =
+ path p ; p := boundingbox parent ;
+ p := boundingbox parent ;
+ numeric w, h ; w := bbwidth(p) ; h := bbheight(p) ;
+ urcorner p shifted (-uniformdeviate w/4,0) --
+ lrcorner p shifted (0,uniformdeviate h/4) --
+ llcorner p shifted (uniformdeviate w/4,0) --
+ ulcorner p shifted (0,-uniformdeviate h/4) -- cycle
+ enddef ;
+
+ vardef MyShape(expr parent) =
+ path p ; p := boundingbox parent ;
+ if MyPageDone :
+ MyPageDone := false ;
+ urcorner p shifted (-EmWidth + -uniformdeviate CutSpace/2,0) --
+ lrcorner p shifted (0,EmWidth + uniformdeviate BottomSpace/2) --
+ llcorner p shifted (EmWidth + uniformdeviate BackSpace/2,0) --
+ ulcorner p shifted (0,-EmWidth + -uniformdeviate TopSpace/2) -- cycle
+ else :
+ MyPageDone := true ;
+ urcorner p shifted (0,-EmWidth + -uniformdeviate TopSpace/2) --
+ lrcorner p shifted (-EmWidth + -uniformdeviate CutSpace/2,0) --
+ llcorner p shifted (0,EmWidth + uniformdeviate BottomSpace/2) --
+ ulcorner p shifted (EmWidth + uniformdeviate BackSpace/2,0) -- cycle
+ fi
+ enddef ;
+
+ vardef MyMakeOne =
+ MyLastOne := MyShape(Page) ;
+ enddef ;
+
+ vardef MyAddOne =
+ addto MySoFar also image(fill MyLastOne withcolor MyPageColor ; ) ;
+ enddef ;
+
+ vardef MyDrawOne =
+ fill MyLastOne withcolor black ;
+ fill MyLastOne withcolor MyPageColor ;
+ enddef ;
+
+ vardef MyDrawPage =
+ draw MySoFar ;
+ enddef ;
+
+ vardef MySetSteps =
+ path l, r ; numeric s ; path ll[], rr[] ; path t ;
+ l := point 2 of MyLastOne -- point 3 of MyLastOne ;
+ r := point 0 of MyLastOne -- point 1 of MyLastOne ;
+ t := topboundary Field[Text][Text] rightenlarged TextWidth leftenlarged TextWidth ;
+ s := bbheight(Field[Text][Text])/LineHeight + 2 ;
+ t := t shifted (0,-TopSkip) ;
+ for i=1 upto s :
+ ll[i] := t intersectionpoint l ;
+ rr[i] := t intersectionpoint r ;
+ t := t shifted (0,-LineHeight) ;
+ endfor ;
+ MyLeftSteps := for i=1 upto s : ll[i] -- endfor cycle ;
+ MyRightSteps := for i=1 upto s : rr[i] -- endfor cycle ;
+ enddef ;
+
+ vardef MyDrawText(expr txt) =
+ pair a ; a := (point 1 of MyLastOne) - (point 2 of MyLastOne) ;
+ picture p ; p := txt ;
+ p := p
+ shifted (-EmWidth,EmWidth)
+ shifted ulcorner txt
+ shifted point 1 of MyLastOne ;
+ p := p rotatedaround(lrcorner p, radian * tan(ypart a/xpart a)) ;
+ setbounds p to origin -- cycle ;
+ draw p ;
+ enddef ;
+
+ vardef MyDrawTitle(expr txt) =
+ % pair a ; a := (point 2 of MyLastOne) - (point 3 of MyLastOne) ;
+ pair a ; a := (point 3 of MyLastOne) - (point 4 of MyLastOne) ;
+ picture p ;
+ if bbheight(txt) > bbwidth(txt) :
+ p := txt ysized(0.8*TextHeight) ;
+ else :
+ p := txt xsized(0.8*TextWidth) ;
+ fi ;
+ numeric d ; d := arclength(point 2 of MyLastOne -- point 3 of MyLastOne) - bbheight(p) ;
+ p := p
+ shifted (BackSpace,-d/2)
+ shifted -ulcorner p
+ shifted point 3 of MyLastOne ;
+ % p := p rotatedaround(ulcorner p, - radian * tan(xpart a/ypart a)) ;
+ % p := p rotatedaround(ulcorner p, radian * tan(ypart a/xpart a)) ;
+ setbounds p to origin -- cycle ;
+ draw p ;
+ enddef ;
+
+ vardef MyDrawSteps =
+ s := bbheight(Field[Text][Text])/LineHeight + 2 ;
+ for i=1 upto s :
+ draw ll[i] withpen pencircle scaled 1mm ;
+ draw rr[i] withpen pencircle scaled 1mm ;
+ draw ll[i] -- rr[i] ;
+ endfor ;
+ draw Field[Text][Text] ;
+ enddef ;
+
+ fi ;
+\stopMPinitializations
+
+\startuseMPgraphic{initialization}
+ StartPage ;
+ MySoFar := image(fill Page enlarged 12pt withcolor MyPageColor) ;
+ MyMakeOne ;
+ MySetSteps ;
+ StopPage ;
+\stopuseMPgraphic
+
+\appendtoks
+ \startnointerference
+ \useMPgraphic{initialization}
+ \stopnointerference
+\to \everystarttext
+
+\startuseMPgraphic{page}
+ StartPage ;
+ MyDrawPage ;
+ MyDrawOne ;
+ MySetSteps ;
+ MyDrawTitle(textext("\getvariable{document}{title}")) ;
+ MyDrawText(textext("\getvariable{document}{topic}")) ;
+ %
+ % we have multiple runs when we have text
+ %
+% MyDrawSteps ;
+% MyMakeOne ;
+% MySetSteps ;
+ StopPage ;
+\stopuseMPgraphic
+
+\appendtoks
+ \startnointerference
+ \startMPcode
+ MyAddOne ;
+ MyMakeOne ;
+ MySetSteps ;
+ \stopMPcode
+ \stopnointerference
+\to \everyshipout
+
+\defineoverlay[page][\useMPgraphic{page}]
+
+\startuseMPgraphic{symbol}
+ color cc ; cc := MyColor[2] ;
+ path p ; p := MySmallShape(unitsquare scaled (.6*LineHeight)) ;
+ fill p withcolor white ;
+ fill p withcolor cc ;
+\stopuseMPgraphic
+
+\definesymbol[mysymbol][\struttedbox{\useMPgraphic{symbol}}]
+
+\setupitemgroup[itemize][1][symbol=mysymbol]
+
+\setupbackgrounds
+ [page]
+ [background=page]
+
+\startluacode
+ local texdimen = tex.dimen
+ function document.SetParShape()
+ local leftpath = metapost.getclippath("metafun","metafun","clip currentpicture to MyLeftSteps ;")
+ local rightpath = metapost.getclippath("metafun","metafun","clip currentpicture to MyRightSteps ;")
+ local shape = { }
+ for i=1,#leftpath do
+ local left = leftpath[i].x_coord
+ local right = rightpath[i].x_coord
+ local hsize = right - left - (texdimen.backspace + texdimen.cutspace)*number.dimenfactors.bp
+ shape[#shape+1] = string.format("%sbp %sbp",left,hsize)
+ end
+ -- print(table.serialize(shape))
+ -- context.parshape(string.format("%s %s ",#shape,table.concat(shape," ")))
+ context("\\parshape %s %s ",#shape,table.concat(shape," "))
+ end
+\stopluacode
+
+\nopenalties \dontcomplain
+
+\setupwhitespace[none]
+
+\def\StartText#1#2%
+ {\starttext
+ \setvariable{document}{title}{\framed[frame=off,offset=0pt,align=flushleft,foregroundstyle=\tfd\setupinterlinespace]{\begstrut#1\endstrut}}
+ \setvariable{document}{topic}{\tfb#2}
+ \startstandardmakeup
+ % dummy page
+ \stopstandardmakeup
+ \setvariable{document}{title}{}
+ \setvariable{document}{topic}{}}
+
+\def\StopText
+ {\stoptext}
+
+\def\StartItems#1%
+ {\setvariable{document}{topic}{\tfb#1}
+ \startstandardmakeup[top=,bottom=\vss]
+ \startelement[items][title={#1}]%
+ \ctxlua{document.SetParShape()}
+ \StartSteps}
+
+\def\StopItems
+ {\StopSteps
+ \stopelement
+ \stopstandardmakeup}
+
+\def\StartItem
+ {\dontleavehmode
+ \startelement[item]%
+ \llap{\symbol[mysymbol]\quad}% graphic
+ \ignorespaces}
+
+\def\StopItem
+ {\removeunwantedspaces
+ \nobreak
+ \crlf
+ \stopelement
+ \crlf
+ \FlushStep}
+
+\def\ShapeParagraph
+ {\ctxlua{document.SetParShape()}}
+
+% no parshape yet
+
+\def\StartParagraphs#1%
+ {\setvariable{document}{topic}{\tfb#1}
+ \startstandardmakeup[top=,bottom=\vss]
+ %\ctxlua{document.SetParShape()}
+ \startelement[paragraphs]%
+ \StartSteps}
+
+\def\StopParagraphs
+ {\StopSteps
+ \stopelement
+ \stopstandardmakeup}
+
+\def\StartParagraph
+ {\startelement[paragraph]}
+
+\def\StopParagraph
+ {\par
+ \stopelement
+ \FlushStep}
+
+% experiment .. likely to change
+
+\setelementexporttag[items] [nature][display]
+\setelementexporttag[item] [nature][mixed]
+\setelementexporttag[paragraphs][nature][display]
+\setelementexporttag[paragraph] [nature][mixed]
+
+\continueifinputfile{s-pre-69.mkiv}
+
+% finetuning: \StartText{\TEX\ and Reality\vskip2exClashing Mindsets?\vskip1ex}{Bacho\TEX, May 1, 2010}
+
+\StartText{Just\\A Demo}{Bacho\TEX, May 1, 2010}
+
+\StartItems{Quote from Tufte and Ward}
+ \StartItem
+ \input tufte
+ \StopItem
+ \StartItem
+ \input ward
+ \StopItem
+\StopItems
+
+% \dorecurse{20}{
+% \ctxlua{document.SetParShape()}
+% \input tufte
+% \page
+% }
+
+\StopText
+
diff --git a/tex/context/modules/mkiv/s-pre-70.mkiv b/tex/context/modules/mkiv/s-pre-70.mkiv
new file mode 100644
index 000000000..2c8bb2d26
--- /dev/null
+++ b/tex/context/modules/mkiv/s-pre-70.mkiv
@@ -0,0 +1,176 @@
+%D \module
+%D [ file=s-pre-70,
+%D version=2008.04.15,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Presentation Environment 70,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D At the cost of more runtime and a larger output file, we
+%D turn on randomization. The instances are cached in the
+%D MkIV cache, so successive runs use the same shapes.
+
+% \usemodule[punk] \usetypescript[punk] \setupbodyfont[punk,20pt]
+%
+% \EnableRandomPunk
+
+\setupbodyfont[punknova,20pt] % we now use the opentype variant
+
+%D We use the regular screen size paper and layout setup.
+
+\setuppapersize
+ [S6][S6]
+
+\setuplayout
+ [topspace=30pt,
+ backspace=30pt,
+ width=middle,
+ height=fit,
+ header=0pt,
+ footer=0pt,
+ bottomdistance=24pt,
+ bottom=30pt,
+ bottom=18pt,
+ top=0pt]
+
+\setupinterlinespace
+ [top=height,
+ line=1.25\bodyfontsize]
+
+\setupcolors
+ [state=start,
+ textcolor=white]
+
+\setupinteraction
+ [state=start,
+ %click=off,
+ menu=on]
+
+%D We predefine a few palets. Of course you can define more.
+
+\definecolor[punkblue] [r=.4,b=.8,g=.4]
+\definecolor[punkgreen] [r=.4,b=.4,g=.8]
+\definecolor[punkred] [r=.8,b=.4,g=.4]
+\definecolor[punkyellow][r=.6,g=.6,b=.2]
+
+\definepalet [punk-one] [textcolor=punkblue,pagecolor=punkgreen]
+\definepalet [punk-two] [textcolor=punkred,pagecolor=punkyellow]
+\definepalet [punk-three] [textcolor=punkblue,pagecolor=punkyellow]
+\definepalet [punk-one-reverse] [textcolor=punkgreen,pagecolor=punkblue]
+\definepalet [punk-two-reverse] [textcolor=punkyellow,pagecolor=punkred]
+\definepalet [punk-three-reverse] [textcolor=punkyellow,pagecolor=punkblue]
+
+\setuppalet[punk-one]
+
+%D We use a few backgrounds. The hyperlink that invokes the
+%D stepper is hooked into the text background.
+
+\definelayer
+ [page]
+ [width=\paperwidth,
+ height=\paperheight]
+
+\setupbackgrounds
+ [page]
+ [background={color,page},
+ backgroundcolor=pagecolor,
+ setups=pagestuff]
+
+\setupbackgrounds
+ [text]
+ [background={color,invoke},
+ backgroundoffset=12pt,
+ backgroundcolor=textcolor]
+
+%D We need different symbols for itemized lists.
+
+\definesymbol[1][\hbox{\lower1ex\hbox{*}}]
+\definesymbol[2][\endash]
+\definesymbol[3][\letterhash]
+\definesymbol[3][>]
+
+%D We don't want these reversed clicked areas in Acrobat.
+
+\setupinteraction
+ [click=no,
+ color=white, % pagecolor,
+ contrastcolor=white] % pagecolor,
+
+%D We define a rather simple navigational panel at the
+%D bottom
+
+\setupinteractionmenu
+ [bottom]
+ [color=white, % pagecolor,
+ contrastcolor=white, % pagecolor,
+ background=color,
+ backgroundcolor=textcolor,
+ frame=off,
+ height=24pt,
+ left=\hfill,
+ middle=\hskip12pt]
+
+\setupsubpagenumber
+ [state=start]
+
+\startinteractionmenu[bottom]
+ \starttxt
+ \interactionbar
+ [alternative=d,
+ symbol=yes,
+ color=white,
+ contrastcolor=textcolor]
+ \stoptxt
+ \hfilll
+ \startbut [previouspage] < < < \stopbut
+ \startbut [nextpage] > > > \stopbut
+\stopinteractionmenu
+
+%D Instead of the normal symbols we use more punky ones.
+
+\startsymbolset [punk]
+ \definesymbol[previous] [\string<\string<]
+ \definesymbol[somewhere] [\string^\string^]
+ \definesymbol[next] [\string>\string>]
+\stopsymbolset
+
+\setupinteraction[symbolset=punk]
+
+%D Because the font is rather large, we use less whitespace.
+
+\setuphead
+ [chapter]
+ [after={\blank[big]}]
+
+%D Run this file with the command: \type {context --mode=demo s-pre-70}
+%D in order to get an example.
+
+\continueifinputfile{s-pre-70.mkiv}
+
+\usemodule[pre-60] % use the stepper
+
+\starttext
+
+\title {Punk for dummies}
+
+\dorecurse{10} {
+
+ \title{Just a few dummy pages}
+
+ \StartSteps
+ \startitemize[packed]
+ \startitem bla \FlushStep \stopitem
+ \startitem bla bla \FlushStep \stopitem
+ \startitem bla bla bla \FlushStep \stopitem
+ \startitem bla bla bla bla \FlushStep \stopitem
+ \stopitemize
+ \StopSteps
+
+}
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-pre-71.lua b/tex/context/modules/mkiv/s-pre-71.lua
new file mode 100644
index 000000000..bfa45a705
--- /dev/null
+++ b/tex/context/modules/mkiv/s-pre-71.lua
@@ -0,0 +1,63 @@
+if not modules then modules = { } end modules ['steps'] = {
+ version = 1.001,
+ comment = "companion to steps.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.steps = moduledata.steps or { }
+local steps = moduledata.steps
+
+local locations = {
+ 'lefttop',
+ 'middletop',
+ 'righttop',
+ 'middleleft',
+ 'middle',
+ 'middleright',
+ 'leftbottom',
+ 'middlebottom',
+ 'rightbottom',
+}
+
+local done, current, previous, n
+
+function steps.reset_locations()
+ done, current, previous, n = table.tohash(locations,false), 0, 0, 0
+end
+
+function steps.next_location(loc)
+ previous = current
+ n = n + 1
+ loc = loc and loc ~= "" and tonumber(loc)
+ while true do
+ current = loc or math.random(1,#locations)
+ if not done[current] then
+ done[current] = true
+ break
+ end
+ end
+end
+
+function steps.current_location()
+ context(locations[current] or "")
+end
+
+function steps.previous_location()
+ context(locations[previous] or "")
+end
+
+function steps.current_n()
+ context(current)
+end
+
+function steps.previous_n()
+ context(previous)
+end
+
+function steps.step()
+ context(n)
+end
+
+steps.reset_locations()
diff --git a/tex/context/modules/mkiv/s-pre-71.mkiv b/tex/context/modules/mkiv/s-pre-71.mkiv
new file mode 100644
index 000000000..38dae61df
--- /dev/null
+++ b/tex/context/modules/mkiv/s-pre-71.mkiv
@@ -0,0 +1,170 @@
+% engine=luatex
+
+%D \module
+%D [ file=s-pre-71,
+%D version=2008.08.05,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Presentation Environment 71,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D I might improve this module so consider it experimental.
+
+% \enablemode[numbers]
+% \enablemode[paper]
+
+\usemodule[pre-60,abr-02]
+
+\registerctxluafile{s-pre-71}{}
+
+\setupinteraction[state=start,click=off]
+
+\definepapersize[wide][width=900pt,height=600pt]
+
+\setuppapersize[wide][wide]
+
+\setuplayout[page]
+
+% \setupbodyfont[15pt]
+
+\usetypescriptfile[type-hgz]
+\usetypescript[palatino-informal]
+\setupbodyfont[palatino-informal,15pt]
+
+\setupsorting[logo][style=]
+
+\startnotmode[paper]
+ \setupbackgrounds[page][background=color,backgroundcolor=black]
+\stopnotmode
+
+\TransparencyHack
+
+\definelayer[page][width=\paperwidth,height=\paperheight]
+
+\definecolor[TopicColor-1][r=.3,g=.4,b=.5]
+\definecolor[TopicColor-2][r=.3,g=.5,b=.4]
+\definecolor[TopicColor-3][r=.4,g=.3,b=.5]
+\definecolor[TopicColor-4][r=.4,g=.5,b=.3]
+\definecolor[TopicColor-5][r=.5,g=.3,b=.4]
+\definecolor[TopicColor-6][r=.5,g=.4,b=.3]
+\definecolor[TopicColor-7][r=.35,g=.35,b=.6]
+\definecolor[TopicColor-8][r=.6,g=.35,b=.35]
+\definecolor[TopicColor-9][r=.35,g=.6,b=.35]
+
+\definecolor[TopicColor-0][t=.5,a=1,s=.5]
+\definecolor[TopicColor] [s=1]
+
+\setupcolors[state=start]
+\setupcolors[textcolor=TopicColor]
+
+\def\StartTopics
+ {\startstandardmakeup
+ \ctxlua{moduledata.steps.reset_locations()}
+ \doifnotmode{paper}{\StartLocalSteps}}
+
+\def\StopTopics
+ {\doifnotmode{paper}{\StopLocalSteps}
+ \flushlayer[page]
+ \stopstandardmakeup}
+
+\def\StartTopic
+ {\dosingleempty\doStartTopic}
+
+\def\doStartTopic[#1]%
+ {\doifnotmode{paper}{\NextStep}
+ \ctxlua{moduledata.steps.next_location("#1")}
+ \startnotmode[paper]
+ \doifnothing{#1}
+ {\ifcase\ctxlua{moduledata.steps.previous_n()}\else
+ \setlayer
+ [page]
+ [preset=\ctxlua{moduledata.steps.previous_location()}]
+ \bgroup
+ \doifnotmode{paper}{\startviewerlayer[\StepLayer]}%
+ \framed
+ [offset=20pt,
+ strut=no,
+ align=normal,
+ frame=off,
+ height=\dimexpr\paperheight/3\relax,
+ width=\dimexpr\paperwidth/3\relax,
+ background=color,
+ backgroundcolor=TopicColor-0]
+ {}%
+ \doifnotmode{paper}{\stopviewerlayer}%
+ \egroup
+ \fi}
+ \stopnotmode
+ \setlayer
+ [page]
+ [preset=\ctxlua{moduledata.steps.current_location()}]
+ \bgroup
+ \doifnotmode{paper}{\startviewerlayer[\StepLayer]}%
+ \framed
+ [offset=20pt,
+ strut=no,
+ align=\expdoifelse{#1}{}{normal}{middle,lohi},
+ align=\expdoifelse{#1}{}{flushleft,verytolerant}{middle,lohi},
+ frame=off,
+ height=\dimexpr\paperheight/3\relax,
+ width=\dimexpr\paperwidth/3\relax,
+ background=color,
+ backgroundcolor=TopicColor-\ctxlua{moduledata.steps.current_n()}]
+ \bgroup
+ \ignorespaces}
+
+\def\StopTopic
+ {\removeunwantedspaces
+ \egroup
+ \doifnotmode{paper}{\stopviewerlayer}%
+ \egroup
+ \startmode[numbers]
+ \setlayerframed
+ [page]
+ [preset=\ctxlua{moduledata.steps.current_location()}]
+ [height=\dimexpr\paperheight/3\relax,
+ width=\dimexpr\paperwidth/3\relax,
+ frame=off,
+ foregroundstyle=\bfa,
+ align={flushright,low}]
+ {\doifnotmode{paper}{\startviewerlayer[\StepLayer]}%
+ \ctxlua{moduledata.steps.step()}\kern\strutdepth
+ \doifnotmode{paper}{\stopviewerlayer}}
+ \stopmode}
+
+\logo [METAPOST] {MetaPost}
+
+\definefont[TitleFont][SansBold at 60pt]
+\definefont[TempFont] [SansBold at 12pt]
+
+\let\StartText\starttext
+\let\StopText \stoptext
+
+\continueifinputfile{s-pre-71.mkiv}
+
+\starttext
+
+\StartTopics
+ \StartTopic[1] A \StopTopic
+ \StartTopic[5] B \StopTopic
+ \StartTopic[9] C \StopTopic
+\StopTopics
+
+\StartTopics
+ \StartTopic A \StopTopic
+ \StartTopic B \StopTopic
+ \StartTopic C \StopTopic
+ \StartTopic D \StopTopic
+ \StartTopic E \StopTopic
+ \StartTopic F \StopTopic
+ \StartTopic G \StopTopic
+ \StartTopic H \StopTopic
+ \StartTopic I \StopTopic
+\StopTopics
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-present-tiles.mkiv b/tex/context/modules/mkiv/s-present-tiles.mkiv
new file mode 100644
index 000000000..b68a34ef4
--- /dev/null
+++ b/tex/context/modules/mkiv/s-present-tiles.mkiv
@@ -0,0 +1,318 @@
+%D \module
+%D [ file=s-present-tiles, % was s-pre-41
+%D version=2013.03.24,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Presentation Environment Tiles,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D The Bacho\TeX\ 2013 style.
+
+\setupbodyfont[pagella,14.4pt]
+
+\setuppapersize[S6][S6]
+
+\definecolor[primarycolor] [.50(darkblue,darkgreen)]
+\definecolor[secondarycolor][.25(darkblue,darkyellow)]
+\definecolor[mixedcolor] [s=.35]
+
+\setupcolors
+ [textcolor=white]
+
+\setupalign
+ [flushleft,nothyphenated]
+
+\definemeasure[layoutunit][\dimexpr\paperheight/25]
+
+\setuplayout
+ [backspace=\measured{layoutunit},
+ topspace=\measured{layoutunit},
+ width=middle,
+ height=middle,
+ header=0pt,
+ footer=0pt]
+
+\definelayout
+ [content]
+ [backspace=.5\measured{layoutunit},
+ topspace=.5\measured{layoutunit}]
+
+\definemeasure [topiclistwidth] [
+ (\textwidth+\backspace)/
+ \ifcase\numexpr\structurelistsize\relax 1 % 0
+ \or1\or1\or1\or2\or1\or3\or3\or3\or3 % 1 - 9
+ \or3\or3\or3\or3\or3\or3\or4 % 10 - 16
+ \or4\or4\or4\or4\or % 17 - 20
+ \else5 % 21 - 25
+ \fi
+ -\backspace
+]
+
+\definemeasure [topiclistheight] [
+ (\textheight+\topspace)/
+ \ifcase\numexpr\structurelistsize\relax 1 % 0
+ \or1\or2\or3\or2\or5\or2\or3\or3\or3 % 1 - 9
+ \or4\or4\or4\or4\or4\or4\or4 % 10 - 16
+ \or5\or5\or5\or5\or5 % 17 - 20
+ \else5 % 21 - 25
+ \fi
+ -\topspace
+]
+
+\definemeasure [topiclistfontsize] [
+ \ifcase\numexpr\structurelistsize\relax 2.5 % 0
+ \or1.4\or1.4\or1.4\or1.4\or1.4\or1.4\or1.4\or1.4\or1.4 % 1 - 9
+ \or1.3\or1.3\or1.3\or1.3\or1.3\or1.3\or1.3 % 10 - 16
+ \or1.2\or1.2\or1.2\or1.2\or1.2 % 17 - 20
+ \else1 % 21 - 25
+ \fi
+ \bodyfontsize
+]
+
+\definemeasure [topiclistfont] [\measured{topiclistfontsize}]
+\definemeasure [topictitlefont] [1.2\measured{layoutunit}]
+\definemeasure [titlepagefont] [2\measured{layoutunit}]
+
+\predefinefont[MyTopicTitleFont][SerifBold*default at \measure{topictitlefont}]
+\predefinefont[MyTopicListFont] [SerifBold*default at \measure{topiclistfont}]
+
+\defineframed
+ [topiclistentry]
+ [width=\measure{topiclistwidth},
+ height=\measure{topiclistheight},
+ background=color,
+ frame=off,
+ foregroundstyle=MyTopicListFont,
+ backgroundcolor=primarycolor,
+ foregroundcolor=white]
+
+\setupwhitespace
+ [big]
+
+\setupblank
+ [big]
+
+\setupinteraction
+ [state=start,
+ color=,
+ contrastcolor=,
+ click=off]
+
+\definemakeup
+ [topic]
+ [standard]
+ [top=,bottom=]
+
+\definemakeup
+ [content]
+ [standard]
+ [top=,bottom=]
+
+\startsetups[topic:start]
+ \starttopicmakeup
+\stopsetups
+
+\startsetups[topic:stop]
+ \vfill
+ \stoptopicmakeup
+\stopsetups
+
+\definehead
+ [topic]
+ [chapter]
+ [before=,
+ style=MyTopicTitleFont,
+ page=,
+ number=off,
+ interaction=list,
+ beforesection=\directsetup{topic:start},
+ aftersection=\directsetup{topic:stop}]
+
+\setuplist
+ [topic]
+ [alternative=topic,
+ interaction=all]
+
+\definelistalternative
+ [topic]
+ [alternative=horizontal,
+ renderingsetup=topic:list]
+
+\startsetups[topic:list]
+ \dontleavehmode
+ \startcurrentlistentrywrapper
+ \topiclistentry{\currentlistentrytitle}
+ \stopcurrentlistentrywrapper
+ \blankspace
+\stopsetups
+
+\defineoverlay[nextpage][\overlaybutton{nextpage}]
+\defineoverlay[prevpage][\overlaybutton{prevpage}]
+\defineoverlay[menupage][\overlaybutton{menupage}]
+\defineoverlay[homepage][\overlaybutton{homepage}]
+
+\setupbackgrounds
+ [text]
+ [backgroundoffset=.5\measured{layoutunit}]
+
+% begin of buttons
+
+\defineframed
+ [bb]
+ [width=\dimexpr\overlaywidth/10,
+ height=\dimexpr\overlayheight/10,
+ background=color,
+ foregroundstyle=\bfa,
+ frame=off]
+
+\defineframed[nb] [bb] [empty=yes]
+\defineframed[rb] [bb] [backgroundcolor=white,foregroundcolor=secondarycolor]
+\defineframed[db] [bb] [backgroundcolor=mixedcolor,foregroundcolor=white]
+
+\definepushbutton [prev]
+\definepushbutton [next]
+\definepushbutton [menu]
+\definepushbutton [home]
+
+\definepushsymbol [prev] [n] [\nb{prev}]
+\definepushsymbol [prev] [r] [\rb{prev}]
+\definepushsymbol [prev] [d] [\db{prev}]
+
+\definepushsymbol [next] [n] [\nb{next}]
+\definepushsymbol [next] [r] [\rb{next}]
+\definepushsymbol [next] [d] [\db{next}]
+
+\definepushsymbol [menu] [n] [\nb{menu}]
+\definepushsymbol [menu] [r] [\rb{menu}]
+\definepushsymbol [menu] [d] [\db{menu}]
+
+\definepushsymbol [home] [n] [\nb{home}]
+\definepushsymbol [home] [r] [\rb{home}]
+\definepushsymbol [home] [d] [\db{home}]
+
+\definelayer
+ [buttons]
+ [width=\dimexpr\textwidth+\measured{layoutunit}\relax,
+ height=\dimexpr\textheight+\measured{layoutunit}\relax] % a setups
+
+\startsetups [document:buttons]
+
+ \setlayer
+ [buttons]
+ [preset=leftbottom,offset=-.25\measured{layoutunit}]
+ {\pushbutton[prev][previouspage]}
+
+ \setlayer
+ [buttons]
+ [preset=rightbottom,offset=-.25\measured{layoutunit}]
+ {\pushbutton[next][nextpage]}
+
+ \setlayer
+ [buttons]
+ [preset=lefttop,offset=-.25\measured{layoutunit}]
+ {\pushbutton[home][homepage]}
+
+ \setlayer
+ [buttons]
+ [preset=righttop,offset=-.25\measured{layoutunit}]
+ {\pushbutton[menu][menupage]}
+
+\stopsetups
+
+\defineoverlay[setbuttons][\directsetup{document:buttons}]
+
+% end of buttons
+
+\defineframed
+ [conclusion]
+ [location=low,
+ width=max,
+ align={flushleft,lohi},
+ background=color,
+ backgroundcolor=white,
+ foregroundcolor=secondarycolor]
+
+\startsetups [document:titlepage]
+
+ \definebodyfontenvironment
+ [\measure{titlepagefont}]
+
+ \setupbackgrounds
+ [page]
+ [background={color,menupage},
+ backgroundcolor=secondarycolor]
+
+ \setupbackgrounds
+ [text]
+ [background={color,menupage},
+ backgroundcolor=secondarycolor]
+
+ \starttopicmakeup[reference=homepage]
+
+ \switchtobodyfont[\measure{titlepagefont}]
+
+ \let\\=\crlf
+
+ \startalignment[flushleft]
+ \bf
+ \setupinterlinespace
+ \begstrut\getvariable{document}{title}\endstrut\par
+ \stopalignment
+
+ \vfill
+
+ \startalignment[flushright]
+ \bf
+ \setupinterlinespace
+ \begstrut\getvariable{document}{subtitle}\endstrut\par
+ \stopalignment
+
+ \stoptopicmakeup
+
+ \setupbackgrounds
+ [page]
+ [background={color,homepage},
+ backgroundcolor=secondarycolor]
+
+ \setupbackgrounds
+ [text]
+ [background={color,menupage},
+ backgroundcolor=secondarycolor]
+
+ \startcontentmakeup[reference=menupage]
+
+ \setupalign[tolerant,stretch,paragraph]
+ \flexiblebaselines
+ \placelist[topic][criterium=text]
+
+ \stopcontentmakeup
+
+ \setupbackgrounds
+ [page]
+ % [background={color,menupage},
+ [background={color,invoke},
+ backgroundcolor=primarycolor]
+
+ \setupbackgrounds
+ [text]
+ % [background={color,nextpage,setbuttons,buttons},
+ [background={color,setbuttons,buttons},
+ backgroundcolor=secondarycolor]
+
+\stopsetups
+
+\setupdocument
+ [before=\directsetup{document:titlepage}]
+
+\continueifinputfile{s-present-tiles.mkiv}
+
+\startdocument[title=Whatever We\\Want Here,subtitle=Whatever We\\Want There]
+
+ \dorecurse{12}{\starttopic[title=Topic #1]\input tufte \stoptopic}
+
+\stopdocument
diff --git a/tex/context/modules/mkiv/s-references-show.mkiv b/tex/context/modules/mkiv/s-references-show.mkiv
new file mode 100644
index 000000000..72cccce54
--- /dev/null
+++ b/tex/context/modules/mkiv/s-references-show.mkiv
@@ -0,0 +1,132 @@
+%D \module
+%D [ file=s-references-show.mkiv,
+%D version=2015.04.13,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Reference Checking,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[references-show]
+
+\unprotect
+
+% \enabletrackers[nodes.references,nodes.destinations] % also shows areas
+
+\enabletrackers[nodes.references.show,nodes.destinations.show]
+
+\enablehiddenbackground % trick
+
+\edef\hiddenbackgroundlist{\hiddenbackgroundlist,trace-references}
+
+\defineoverlay
+ [trace-references]
+ [\directsetup{trace:references:onpage}]
+
+\startluacode
+ local pagelist = structures.references.tracedpages
+
+ function commands.getreferencesonpage(n)
+ n = tonumber(n)
+ if n then
+ local pagedata = pagelist[n]
+ if pagedata then
+ context("%s references",#pagedata)
+ context.blank()
+ for i=1,#pagedata do
+ local details = pagedata[i]
+ local prefix = details[1]
+ local reference = details[2]
+ -- local internal = details[3]
+ -- context("%04i = %s : %s",internal,prefix == "" and "-" or prefix,reference)
+ context("%s : %s",prefix == "" and "-" or prefix,reference)
+ context.par()
+ end
+ else
+ context("no references")
+ end
+ else
+ context("no valid page")
+ end
+ end
+
+\stopluacode
+
+\definecolor
+ [trace:references:onpage]
+ [b=.5,a=1,t=.25]
+
+\defineframed
+ [trace:references:onpage]
+ [\c!offset=2\exheight,
+ %\c!foregroundstyle=\infofont,
+ \c!frame=\v!off,
+ \c!background=\v!color,
+ \c!backgroundcolor=trace:references:onpage,
+ \c!align=\v!normal]
+
+\startsetups trace:references:onpage
+ \vbox to \vsize \bgroup
+ \infofont
+ \vskip\dimexpr-\topspace-\headerheight+2\exheight\relax
+ \hbox to \hsize \bgroup
+ \doifoddpageelse\hss{\hskip\dimexpr-\cutspace+2\exheight\relax}%
+ \directlocalframed [
+ trace:references:onpage
+ ] {
+ \ctxcommand{getreferencesonpage(\the\realpageno)}
+ }
+ \doifoddpageelse{\hskip\dimexpr-\cutspace+2\exheight\relax}\hss
+ \egroup
+ \vss
+ \egroup
+\stopsetups
+
+\protect
+
+\continueifinputfile{s-references-show.mkiv}
+
+\usemodule[art-01]
+
+\setupinteraction
+ [state=start]
+
+\setuppagenumbering
+ [alternative=doublesided]
+
+\starttext
+
+\title {Contents}
+
+\placelist[chapter]
+
+\setupreferenceprefix[zero]
+
+\chapter[crap]{foo}
+
+\setupreferenceprefix[one]
+
+test \pagereference[whatever]
+
+\dorecurse{5}{
+ \placefigure
+ [here][bar 1.#1]
+ {xx}{\framed{xx #1}}
+}
+
+\setupreferenceprefix[two]
+
+\dorecurse{5}{
+ \placefigure
+ [here][bar 2.#1]
+ {xx}{\framed{xx #1}}
+}
+
+\in{checked}[bar 1.1]
+\in{checked}[bar 2.1]
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-reg-01.mkiv b/tex/context/modules/mkiv/s-reg-01.mkiv
new file mode 100644
index 000000000..36434c30c
--- /dev/null
+++ b/tex/context/modules/mkiv/s-reg-01.mkiv
@@ -0,0 +1,60 @@
+%D \module
+%D [ file=s-reg-01,
+%D version=2005.04.25,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Extra Regime Support,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D \macros
+%D {showregime}
+%D
+%D This code is moved from the kernel to here. (We could make it
+%D auto-loadable).
+
+\startluacode
+--[[ldx--
+<p>The following code is rather <l n='context'/> specific.</p>
+--ldx]]--
+
+function regimes.show(regime)
+ regime = regimes.synonyms[regime] or regime
+ local r = regimes.data[regime]
+ if r then
+ local chardata = characters.data
+ context.starttabulate { "|rT|T|rT|lT|lT|lT|" }
+ for k, v in ipairs(r) do
+ local chr = chardata[v]
+ if chr then
+ context.NC() context(k)
+ context.NC() context.getvalue(chr.contextname])
+ context.NC() context("%U+05X",v)
+ context.NC() context(chr.contextname)
+ context.NC() context(chr.category)
+ context.NC() context(chr.description)
+ context.NC() context.NR()
+ else
+ -- can't happen
+ end
+ end
+ context.stoptabulate()
+ else
+ context("unknown regime %s",regime)
+ end
+end
+\stopluacode
+
+\unprotect
+
+\def\showregime
+ {\dosingleempty\doshowregime}
+
+\def\doshowregime[#1]%
+ {\ctxlua{regimes.show("#1")}}
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/s-set-31.mkiv b/tex/context/modules/mkiv/s-set-31.mkiv
new file mode 100644
index 000000000..53037a78d
--- /dev/null
+++ b/tex/context/modules/mkiv/s-set-31.mkiv
@@ -0,0 +1,118 @@
+% show missing translations
+
+\startluacode
+function interfaces.show_missing(whatinterfaces)
+ whatinterfaces = whatinterfaces or interfaces.interfaces
+ local list = dofile(resolvers.find_file("mult-def.lua"))
+ local NC, NR, HL = context.NC, context.NR, context.HL
+ for k,v in table.sortedhash(list) do
+ context.title(k)
+ context.starttabulate { string.rep("|",#whatinterfaces+1) }
+ for i=1,#whatinterfaces do
+ NC()
+ context(whatinterfaces[i])
+ end
+ NC() NR() HL()
+ for kk, vv in next, v do
+ if not string.find(kk,"subsub") then
+ local okay = true
+ for i=1,#whatinterfaces do
+ local int = whatinterfaces[i]
+ local str = vv[int]
+ if not str or (int ~= "en" and str == vv.en) then
+ okay = false
+ break
+ end
+ end
+ if not okay then
+ for i=1,#whatinterfaces do
+ local int = whatinterfaces[i]
+ local str = vv[int]
+ context.NC()
+ if not str then
+ -- nothing
+ elseif int == "en" then
+ context.color( { "darkgreen" }, str )
+ elseif str == vv.en then
+ context.color( { "darkred" }, str )
+ elseif int == "pe" then
+ context("\\righttoleft " .. str)
+ else
+ context(str)
+ end
+ end
+ NC() NR()
+ end
+ end
+ end
+ context.stoptabulate()
+ end
+end
+
+function interfaces.show_missing_messages(whatinterfaces)
+ whatinterfaces = whatinterfaces or interfaces.interfaces
+ local list = dofile(resolvers.find_file("mult-mes.lua"))
+ local NC, NR, HL = context.NC, context.NR, context.HL
+ for k,v in table.sortedhash(list) do
+ context.title("message: " .. k)
+ context.starttabulate { "|l|" .. string.rep("pl|",#whatinterfaces) }
+ NC()
+ for i=1,#whatinterfaces do
+ NC()
+ context(whatinterfaces[i])
+ end
+ NC() NR() HL()
+ local sorted = table.sortedkeys(v)
+ for i=1,#sorted do
+ local kk = sorted[i]
+ local vv = v[kk]
+ if kk ~= "files" then
+ local okay = true
+ for i=1,#whatinterfaces do
+ local int = whatinterfaces[i]
+ local str = vv[int]
+ if not str or (int ~= "en" and str == vv.en) then
+ okay = false
+ break
+ end
+ end
+ if not okay then
+ context.NC()
+ context(kk)
+ for i=1,#whatinterfaces do
+ local int = whatinterfaces[i]
+ local str = vv[int]
+ NC()
+ if not str then
+ -- nothing
+ elseif int == "en" then
+ context.color( { "darkgreen" }, str )
+ elseif str == vv.en then
+ context.color( { "darkred" }, str )
+ elseif int == "pe" then
+ context("\\righttoleft " .. str)
+ else
+ context(str)
+ end
+ end
+ NC() NR()
+ end
+ end
+ end
+ context.stoptabulate()
+ end
+end
+\stopluacode
+
+\starttext
+
+\setupbodyfont[dejavu,7pt,tt]
+\setuppapersize[A4,landscape][A4,landscape]
+\setuplayout[backspace=.5cm,width=middle,topspace=.5cm,height=middle,header=1cm,footer=0cm]
+
+\startluacode
+ interfaces.show_missing() -- { "en","nl" }
+ interfaces.show_missing_messages() -- { "en","nl" }
+\stopluacode
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-sql-tables.lua b/tex/context/modules/mkiv/s-sql-tables.lua
new file mode 100644
index 000000000..434103541
--- /dev/null
+++ b/tex/context/modules/mkiv/s-sql-tables.lua
@@ -0,0 +1,152 @@
+if not modules then modules = { } end modules ['s-languages-counters'] = {
+ version = 1.001,
+ comment = "companion to s-languages-counters.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+require("util-tpl")
+require("util-sql")
+require("util-sql-tracers")
+
+moduledata = moduledata or { }
+moduledata.sql = moduledata.sql or { }
+moduledata.sql.tables = moduledata.sql.tables or { }
+
+local context = context
+
+function moduledata.sql.showfields(specification) -- not that sql specific
+ local data = specification.data
+ if data and #data > 0 then
+ local keys = specification.order or table.sortedkeys(data[1])
+ local align = specification.align
+ local template = "|"
+ if type(align) == "table" then
+ for i=1,#keys do
+ template = template .. (align[keys[i]] or "c") .. "|"
+ end
+ else
+ template = template .. string.rep((align or "c").. "|",#keys)
+ end
+ context.starttabulate { template }
+ context.NC()
+ for i=1,#keys do
+ context(keys[i])
+ context.NC()
+ end
+ context.NR()
+ context.HL()
+ for i=specification.first or 1,specification.last or #data do
+ local d = data[i]
+ context.NC()
+ for i=1,#keys do
+ context(d[keys[i]])
+ context.NC()
+ end
+ context.NR()
+ end
+ context.stoptabulate()
+ end
+end
+
+function moduledata.sql.validpresets(presets)
+ local okay = true
+ if presets.database == "" then
+ context("No database given.")
+ context.blank()
+ okay = false
+ end
+ if presets.password == "" then
+ context("No password given")
+ context.blank()
+ okay = false
+ end
+ return okay
+end
+
+function moduledata.sql.tables.showdefined(presets) -- key=value string | { presets = "name" } | { presets }
+
+ if type(presets) == "string" then
+ local specification = interfaces.checkedspecification(presets)
+ if specification.presets then
+ presets = table.load(specification.presets) or { }
+ end
+ end
+
+ if type(presets.presets) == "string" then
+ presets = table.load(presets.presets) or { }
+ end
+
+ if not moduledata.sql.validpresets(presets) then
+ return
+ end
+
+ local sql_tables = utilities.sql.tracers.gettables(presets)
+
+ context.starttitle { title = presets.database }
+
+ for name, fields in table.sortedhash(sql_tables) do
+
+ context.startsubject { title = name }
+
+ context.starttabulate { format = "|l|l|l|l|l|p|" }
+ context.FL()
+ context.NC() context.bold("field")
+ context.NC() context.bold("type")
+ context.NC() context.bold("default")
+ context.NC() context.bold("null")
+ context.NC() context.bold("key")
+ context.NC() context.bold("extra")
+ context.NC() context.NR()
+ context.TL()
+ for i=1,#fields do
+ local field = fields[i]
+ context.NC() context(field.field)
+ context.NC() context(field.type)
+ context.NC() context(field.default)
+ context.NC() context(field.null)
+ context.NC() context(field.key)
+ context.NC() context(field.extra)
+ context.NC() context.NR()
+ end
+ context.LL()
+ context.stoptabulate()
+
+ context.stopsubject()
+ end
+
+ context.stoptitle()
+
+end
+
+function moduledata.sql.tables.showconstants(list)
+
+ context.starttitle { title = "Constants" }
+
+ for name, fields in table.sortedhash(list) do
+
+ if type(fields) == "table" and #fields > 0 then
+
+ context.startsubject { title = name }
+
+ context.starttabulate { format = "|l|l|" }
+ for i=0,#fields do
+ local field = fields[i]
+ if field then
+ context.NC() context(i)
+ context.NC() context(field)
+ context.NC() context.NR()
+ end
+ end
+ context.stoptabulate()
+
+ context.stopsubject()
+
+ end
+
+ end
+
+ context.stoptitle()
+
+end
diff --git a/tex/context/modules/mkiv/s-sql-tables.mkiv b/tex/context/modules/mkiv/s-sql-tables.mkiv
new file mode 100644
index 000000000..4c8bcddac
--- /dev/null
+++ b/tex/context/modules/mkiv/s-sql-tables.mkiv
@@ -0,0 +1,30 @@
+%D \module
+%D [ file=s-sql-tables,
+%D version=2012.07.12,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=SQL,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[sql-tables]
+
+\registerctxluafile{s-sql-tables}{}
+
+\doifsomething {\currentmoduleparameter{method}}{
+ \ctxlua{utilities.sql.setmethod("\currentmoduleparameter{method}")}
+}
+
+\doifsomething {\currentmoduleparameter{server}} {
+ \ctxlua{utilities.sql.setserver("\currentmoduleparameter{server}")}
+}
+
+\installmodulecommandluasingle \showdefinedsqltables {moduledata.sql.tables.showdefined}
+
+\stopmodule
+
+
diff --git a/tex/context/modules/mkiv/s-structure-sections.mkiv b/tex/context/modules/mkiv/s-structure-sections.mkiv
new file mode 100644
index 000000000..daaab5abc
--- /dev/null
+++ b/tex/context/modules/mkiv/s-structure-sections.mkiv
@@ -0,0 +1,80 @@
+%D \module
+%D [ file=s-structure-sections,
+%D version=2015.02.02,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Show Structure Sections,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+\startluacode
+ local context = context
+ local ctx_NC, ctx_NR = context.NC, context.NR
+ local ctx_bold = context.bold
+
+ structures.tracers = structures.tracers or { }
+
+ function structures.tracers.showsections()
+
+ local list = structures.sections.registered
+ local keys = table.keys(list)
+ table.sort(keys,function(a,b)
+ local la, lb = list[a].level, list[b].level
+ if la == lb then
+ return a < b
+ else
+ return la < lb
+ end
+ end)
+ context.start()
+ context.switchtobodyfont { "tt" }
+ context.starttabulate { "||c||||" }
+ context.FL()
+ ctx_NC() ctx_bold("name")
+ ctx_NC() ctx_bold("level")
+ ctx_NC() ctx_bold("parent")
+ ctx_NC() ctx_bold("section")
+ ctx_NC() ctx_bold("coupling")
+ ctx_NC() context.NR()
+ context.ML()
+ for i=1,#keys do
+ local k = keys[i]
+ local v = list[k]
+ ctx_NC() ctx_bold(k)
+ ctx_NC() context(v.level)
+ ctx_NC() context(v.parent)
+ ctx_NC() context(v.section)
+ ctx_NC() context(v.coupling)
+ ctx_NC() context.NR()
+ end
+ context.LL()
+ context.stoptabulate()
+ context.stop()
+
+ end
+\stopluacode
+
+
+\starttexdefinition showstructuresections
+
+ % no settings yet
+
+ \ctxlua{structures.tracers.showsections()}
+
+\stoptexdefinition
+
+\protect
+
+\continueifinputfile{s-structure-sections.mkiv}
+
+\starttext
+
+ \showstructuresections
+
+\stoptext
diff --git a/tex/context/modules/mkiv/s-syntax.mkiv b/tex/context/modules/mkiv/s-syntax.mkiv
new file mode 100644
index 000000000..96312f771
--- /dev/null
+++ b/tex/context/modules/mkiv/s-syntax.mkiv
@@ -0,0 +1,96 @@
+%D \module
+%D [ file=s-syntax, % was: s-syn-01,
+%D version=0000.00.00,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Preliminary Syntax Stuff,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is needed for the \METAFUN\ manual: quite old code that I would do
+%D differently nowadays.
+
+\unprotect
+
+\unexpanded\def\module_syntax_Indent #1{\ifvmode\noindent\hbox to 2em{\hss#1}\else#1\fi}
+\unexpanded\def\module_syntax_Sugar #1{\removeunwantedspaces\kern.25em{#1}\kern.25em\ignorespaces}
+\unexpanded\def\module_syntax_Something #1{\Sugar{\mathematics{\langle\hbox{#1}\rangle}}}
+\unexpanded\def\module_syntax_Lbrace {\Sugar{\tttf\leftargument}}
+\unexpanded\def\module_syntax_Rbrace {\Sugar{\tttf\rightargument}}
+\unexpanded\def\module_syntax_Lparent {\Sugar{\tttf(}}
+\unexpanded\def\module_syntax_Rparent {\Sugar{\tttf)}}
+\unexpanded\def\module_syntax_Lbracket {\Sugar{\tttf[}}
+\unexpanded\def\module_syntax_Rbracket {\Sugar{\tttf]}}
+\unexpanded\def\module_syntax_Or {\Sugar{\mathematics{\vert}}}
+\unexpanded\def\module_syntax_Optional #1{\Sugar{\mathematics{[\hbox{#1}]}}}
+\unexpanded\def\module_syntax_Means {\Sugar{\mathematics{\rightarrow}}}
+\unexpanded\def\module_syntax_Tex #1{\Sugar{\type{#1}}}
+\unexpanded\def\module_syntax_Literal #1{\Sugar{\type{#1}}}
+\unexpanded\def\module_syntax_Syntax #1{\strut\kern-.25em{#1}\kern-.25em}
+\unexpanded\def\module_syntax_Next {\crlf\hbox to 2em{}\nobreak}
+\unexpanded\def\module_syntax_Whatever #1{\Sugar{\mathematics{(\hbox{#1})}}}
+\unexpanded\def\module_syntax_Quote #1{\Sugar{\quote{#1}}}
+\unexpanded\def\module_syntax_Or {\Sugar{\module_syntax_Indent{\mathematics{\vert}}}}
+\unexpanded\def\module_syntax_Means {\Sugar{\module_syntax_Indent{\mathematics{\rightarrow}}}}
+\unexpanded\def\module_syntax_FlaggedLiteral #1{\color[darkred]{\module_syntax_Literal{#1}}}
+\unexpanded\def\module_syntax_FlaggedSomething#1{\module_syntax_Something{\color[darkred]{#1}}}
+
+\unexpanded\def\StartSyntax
+ {\startlines
+ % formatters
+ \let\Indent \module_syntax_Indent
+ \let\Sugar \module_syntax_Sugar
+ \let\Something \module_syntax_Something
+ \let\Lbrace \module_syntax_Lbrace
+ \let\Rbrace \module_syntax_Rbrace
+ \let\Lparent \module_syntax_Lparent
+ \let\Rparent \module_syntax_Rparent
+ \let\Lbracket \module_syntax_Lbracket
+ \let\Rbracket \module_syntax_Rbracket
+ \let\Or \module_syntax_Or
+ \let\Optional \module_syntax_Optional
+ \let\Means \module_syntax_Means
+ \let\Tex \module_syntax_Tex
+ \let\Literal \module_syntax_Literal
+ \let\Syntax \module_syntax_Syntax
+ \let\Next \module_syntax_Next
+ \let\Whatever \module_syntax_Whatever
+ \let\Quote \module_syntax_Quote
+ \let\Or \module_syntax_Or
+ \let\Means \module_syntax_Means
+ \let\FlaggedLiteral \module_syntax_FlaggedLiteral
+ \let\FlaggedSomething\module_syntax_FlaggedSomething
+ % shortcuts
+ \let\FL \module_syntax_FlaggedLiteral
+ \let\FS \module_syntax_FlaggedSomething
+ \let\L \module_syntax_Literal
+ \let\S \module_syntax_Something
+ \let\M \module_syntax_Means
+ \let\O \module_syntax_Or
+ \let\Q \module_syntax_Quote
+ \let\LB \module_syntax_Lbrace
+ \let\RB \module_syntax_Rbrace
+ \let\LP \module_syntax_Lparent
+ \let\RP \module_syntax_Rparent
+ \let\LS \module_syntax_Lbracket
+ \let\RS \module_syntax_Rbracket
+ \let\{ \module_syntax_Lbrace
+ \let\} \module_syntax_Rbrace
+ \let\( \module_syntax_Lparent
+ \let\) \module_syntax_Rparent
+ \let\[ \module_syntax_Lbracket
+ \let\] \module_syntax_Rbracket
+ % precaution
+ \catcode`\#\othercatcode}
+
+\unexpanded\def\StopSyntax
+ {\stoplines}
+
+\unexpanded\def\SyntaxCommand#1%
+ {\csname module_syntax_#1\endcsname}
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/s-typesetting-kerning.mkiv b/tex/context/modules/mkiv/s-typesetting-kerning.mkiv
new file mode 100644
index 000000000..48d81ce36
--- /dev/null
+++ b/tex/context/modules/mkiv/s-typesetting-kerning.mkiv
@@ -0,0 +1,209 @@
+%D \module
+%D [ file=s-typesetting-kerning,
+%D version=2014.12.14,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Show Character Kerning,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+\definecharacterkerning
+ [typesetting-kerning-demo]
+ [factor=.5]
+
+\startbuffer[showcharacterkerning:boxes]
+ \starttextrule{boxes}
+ \showfontkerns
+ \dontcomplain
+ \startlines
+ test \hbox{!} test
+ test\hbox{!} test
+ test \hbox{!}test
+ test:$x$ test
+ \setcharacterkerning[typesetting-kerning-demo]
+ test \hbox{!} test
+ test\hbox{!} test
+ test \hbox{!}test
+ test:$x$ test
+ \stoplines
+ \stoptextrule
+\stopbuffer
+
+\startbuffer[showcharacterkerning:ligatures]
+ \starttextrule{ligatures}
+ \dontcomplain
+ \startlines
+ effe flink effectief efficient fietsen
+ \blank
+ \setcharacterkerning[typesetting-kerning-demo]
+ effe flink effectief efficient fietsen
+ \blank \hsize\zeropoint
+ effe
+ flink
+ effectief
+ efficient
+ fietsen
+ \stoplines
+ \stoptextrule
+\stopbuffer
+
+\startbuffer[showcharacterkerning:discretionaries]
+ \starttextrule{discretionary}
+ \dontcomplain
+ \startlines
+ \hbox{\samplediscretionary}
+ \hbox{xxx\samplediscretionary}
+ \hbox{\samplediscretionary xxx}
+ \hbox{xxx\samplediscretionary xxx}
+ \blank
+ \setcharacterkerning[typesetting-kerning-demo]
+ \hbox{\samplediscretionary}
+ \hbox{xxx\samplediscretionary}
+ \hbox{\samplediscretionary xxx}
+ \hbox{xxx\samplediscretionary xxx}
+ \blank \hsize\zeropoint
+ \samplediscretionary
+ xxx\samplediscretionary
+ \samplediscretionary xxx
+ xxx\samplediscretionary xxx
+ \stoplines
+ \stoptextrule
+\stopbuffer
+
+\startbuffer[showcharacterkerning:explicits]
+ \starttextrule{explicits}
+ \exhyphenchar \hyphenasciicode
+ \preexhyphenchar \lessthanasciicode
+ \postexhyphenchar\morethanasciicode
+ \def\TestDisc
+ {\discretionary
+ {\kern\emwidth<}%
+ {>\kern\emwidth}%
+ {\kern\emwidth=\kern\emwidth}%
+ }
+ \dontcomplain
+ \startlines
+ \hbox{super-charged}
+ \hbox{super\-charged}
+ \hbox{super\TestDisc charged}
+ \hbox{super\discretionary{[}{]}{[]}charged}
+ \blank
+ \setcharacterkerning[typesetting-kerning-demo]
+ \hbox{super-charged}
+ \hbox{super\-charged}
+ \hbox{super\TestDisc charged}
+ \hbox{super\discretionary{[}{]}{[]}charged}
+ \blank \hsize\zeropoint
+ super-charged
+ super\-charged
+ super\TestDisc charged
+ super\discretionary{[}{]}{[]}charged
+ \stoplines
+ \stoptextrule
+\stopbuffer
+
+\starttexdefinition unexpanded showcharacterkerning
+ \getbuffer[showcharacterkerning:boxes]
+ \getbuffer[showcharacterkerning:ligatures]
+ \getbuffer[showcharacterkerning:discretionaries]
+ \getbuffer[showcharacterkerning:explicits]
+\stoptexdefinition
+
+
+\starttexdefinition showcharacterkerningstepscompared #1
+ \definecharacterkerning[crap][factor=\KerningStepFactor]%
+ \setbox0=\ruledhbox{\color[color-1]{#1}\hss}
+ \setbox2=\ruledhbox{\setcharacterkerning[crap]\color[color-2]{#1}}
+ \setbox4=\ruledhbox{\setcharacterkerning[crap]\showfontkerns\showglyphs#1}
+ \xdef\KerningStepPercentage{\ctxlua{context("\letterpercent 0.2f",(1-\number\wd0/\number\wd2)*100)}}
+ \scratchwidth\wd0
+ \vtop\bgroup
+ \hbox{\box0\hskip-\scratchwidth\box2}
+ \par
+ \box4
+ \egroup
+\stoptexdefinition
+
+\starttexdefinition showcharacterkerningsteps [#1]
+
+ \start
+
+ \getdummyparameters
+ [\s!font=Regular,
+ \c!sample={Wat een bende, rommelen met het font design!},
+ \c!text={rommelen},
+ \c!first=00,
+ \c!last=95,
+ \c!step=05,
+ \c!option=, % \v!page
+ #1]
+
+ \doif{\dummyparameter\c!option}\v!page {
+ \startTEXpage[\c!offset=1ex]
+ }
+
+ \definecolor[color-1][r=1,t=.5,a=1]
+ \definecolor[color-2][b=1,t=.5,a=1]
+
+ \definedfont[\dummyparameter\s!font*default sa 1]
+
+ \doif {\dummyparameter\c!option}\v!page {
+ \begingroup
+ \tttf \dummyparameter\s!font\space @ default
+ \endgroup
+ \blank
+ }
+
+ \starttabulate[|cT|l|cT|l|cT|]
+
+ \NC \tt\bf factor \NC \tt\bf sample \NC \tt\bf \letterpercent \NC \tt\bf text \NC \tt\bf \letterpercent \NC \NR \HL
+
+ \dostepwiserecurse {\dummyparameter\c!first} {\dummyparameter\c!last} {\dummyparameter\c!step} {
+ \NC
+ \xdef\KerningStepFactor{\ctxlua{context("\letterpercent 0.3f",####1/1000)}}
+ \KerningStepFactor
+ \NC
+ \showcharacterkerningstepscompared{\dummyparameter\c!sample}
+ \NC
+ \KerningStepPercentage
+ \NC
+ \showcharacterkerningstepscompared{\dummyparameter\c!text}
+ \NC
+ \KerningStepPercentage
+ \NC \NR
+ }
+
+ \stoptabulate
+
+ \doif{\dummyparameter\c!option}\v!page {
+ \stopTEXpage
+ }
+
+ \stop
+
+\stoptexdefinition
+
+\protect
+
+\continueifinputfile{s-typesetting-kerning.mkiv}
+
+\starttext
+
+ % \showcharacterkerning
+
+ \showcharacterkerningsteps[font=file:FuturaStd-Book.otf,option=page]
+ \showcharacterkerningsteps[font=file:FuturaStd-Medium.otf,option=page]
+ \showcharacterkerningsteps[font=file:FuturaStd-Bold.otf,option=page]
+ \showcharacterkerningsteps[font=file:FuturaStd-heavy.otf,option=page]
+
+\stoptext
+
+% {\hsize1mm efficient\discretionary{\kern1pt!\kern1pt}{\kern1pt!\kern1pt}{\kern1pt!\kern1pt}efficient\par}
+% {\hsize1mm\definedfont[Regular]\setcharacterkerning[typesetting-kerning-demo]efficient\-efficient\par}
+
diff --git a/tex/context/modules/mkiv/s-youless.mkiv b/tex/context/modules/mkiv/s-youless.mkiv
new file mode 100644
index 000000000..e15973b9c
--- /dev/null
+++ b/tex/context/modules/mkiv/s-youless.mkiv
@@ -0,0 +1,171 @@
+%D \module
+%D [ file=s-youless,
+%D version=2013.11.12,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Youless Graphics,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is experimental code. When I have collected enough data I will make the
+%D graphics nicer and provide some more.
+%D
+%D The Jouless can serve web pages but there is not much detail in them. They also are
+%D somewhat bad \HTML, like unquoted attributes and so. We don't need this anyway as we
+%D can also fetch data directly. The data is collected using a dedicated helper script
+%D (of course we could just call it as module too). The data is fetched from the Jouless
+%D device using queries (currently we use json, but a more direct parsing of data might
+%D be more efficient). The data is converted into a proper \LUA\ table and saved (merged).
+
+% in cronjob on one of the servers:
+%
+% mtxrun --script youless --collect --host=192.168.2.50 --watt youless-watt.lua
+% mtxrun --script youless --collect --host=192.168.2.50 --kwk youless-kwh.lua
+
+\startluacode
+
+ require("util-you")
+
+ moduledata.youless = { }
+
+ function moduledata.youless.kwh(specification)
+ -- todo
+ end
+
+ function moduledata.youless.watt(specification)
+
+ local year = tonumber(specification.year) or os.today().year
+ local data = table.load(specification.filename or "youless-watt.lua")
+
+ if not data or data.variant ~= "watt" then
+ context("invalid variant")
+ return
+ end
+
+ utilities.youless.analyze(data)
+
+ -- for the moment no specific font scaling
+
+ local years = data.years
+
+ if not years then
+ context("no years")
+ return
+ end
+
+ for y=year,year do
+
+ local year = years[y]
+ local scale = 20
+ local mark = 3
+ local maxwatt = specification.maxwatt or year.maxwatt
+
+ for m=1,12 do
+ local month = year.months[m]
+ if month then
+ context.startMPpage { offset = "10pt" }
+ context("linecap := butt; pickup pencircle scaled .5")
+
+ for i=0,(math.div(maxwatt,1000)+1)*1000,100 do
+ context("draw (%s,%s) -- (%s,%s) withcolor .6white ;",0,i/scale,31 * 24,i/scale)
+ end
+
+ context("draw (0,%s) -- (31 * 24,%s) dashed dashpattern(on 6 off 6) withcolor darkgreen withpen pencircle scaled 1 ;",year.watt /scale,year.watt /scale)
+ context("draw (0,%s) -- (31 * 24,%s) dashed dashpattern(off 6 on 6) withcolor darkred withpen pencircle scaled 1 ;",month.watt/scale,month.watt/scale)
+
+ local days = month.days
+ if days then
+ local nd = os.nofdays(y,m)
+ for d=1,nd do
+ local day = days[d]
+ local xoffset = (d-1) * 24
+ local wd = os.weekday(d,m,y)
+ local weekend = wd == 1 or wd == 7
+ if not weekend then
+ -- okay
+ elseif mark == 1 then
+ context("draw (%s,%s) -- (%s,%s) ; ",xoffset, -17.5,xoffset, -32.5)
+ context("draw (%s,%s) -- (%s,%s) ; ",xoffset+24,-17.5,xoffset+24,-32.5)
+ elseif mark == 2 then
+ context("draw (%s,%s) -- (%s,%s) ; ",xoffset, -17.5,xoffset+24,-17.5)
+ context("draw (%s,%s) -- (%s,%s) ; ",xoffset, -32.5,xoffset+24,-32.5)
+ elseif mark == 3 then
+ context("draw unitsquare xysized (%s,%s) shifted (%s,%s) ; ",24,15,xoffset,-32.5)
+ end
+ context([[draw textext("%s") shifted (%s,%s) ; ]],d,xoffset + 12,-25)
+ if day then
+ for h=0,23 do
+ local hours = day.hours
+ if hours then
+ local hour = hours[h]
+ if hour then
+ local dx = xoffset + h
+ local dy = hour.watt/scale
+ local dm = hour.maxwatt/scale
+ context("draw (%s,%s) -- (%s,%s) withcolor %s ; ",dx, 0,dx,dy,weekend and "darkmagenta" or "darkblue")
+ context("draw (%s,%s) -- (%s,%s) withcolor %s ; ",dx,dy,dx,dm,"darkgray")
+ end
+ end
+ end
+ end
+ end
+ for d=0,30 do
+ local xoffset = d * 24
+ context("draw (%s,%s) -- (%s,%s) withcolor darkgray ; ",xoffset+ 0,0,xoffset+ 0,-10)
+ context("draw (%s,%s) -- (%s,%s) withcolor darkgray ; ",xoffset+ 6,0,xoffset+ 6,-2.5)
+ context("draw (%s,%s) -- (%s,%s) withcolor darkgray ; ",xoffset+12,0,xoffset+12,-5)
+ context("draw (%s,%s) -- (%s,%s) withcolor darkgray ; ",xoffset+18,0,xoffset+18,-2.5)
+ end
+ local xoffset = 31 * 24
+ context("draw (%s,%s) -- (%s,%s) withcolor darkgray ; ",xoffset,0,xoffset,-10)
+ end
+
+ local max = (math.div(maxwatt,1000)+1)
+
+ for i=0,max*1000,1000 do
+ context([[draw textext.lft("%s") shifted (%s,%s) ; ]],i,-10,i/scale)
+ context("draw (%s,%s) -- (%s,%s) withcolor .2white ;",0,i/scale,31 * 24,i/scale)
+ end
+
+ context([[draw textext("\strut\month{%s}\enspace%s") shifted (%s,%s) ; ]],m, y, 31 * 24 / 2, -50)
+ context([[draw textext.lft("watt") shifted (%s,%s) ; ]],-10,-25)
+
+ context.stopMPpage()
+ else
+ -- maybe placeholder
+ end
+ end
+
+ end
+
+ end
+
+\stopluacode
+
+\continueifinputfile{s-youless.mkiv}
+
+\setupbodyfont[dejavu] % smaller sizes also look ok
+
+% printer (oce) : > 3000 W startup (900 W idle, 2000 W printing)
+% coffeemaker : 1500 W when heating
+
+% baseline day : 2250 W (servers, airco, workstations, routers, switches, heating, etc)
+% baseline night : 1750 W
+
+\starttext
+
+ \startluacode
+
+ -- os.execute([[mtxrun --script youless --collect --watt "c:/data/system/youless/data/youless-watt.lua"]])
+ -- os.execute([[mtxrun --script youless --collect --watt --nobackup "c:/data/system/youless/data/youless-watt.lua"]])
+ -- moduledata.youless.watt { year = 2013, filename = "c:/data/system/youless/data/youless-watt.lua" }
+
+ moduledata.youless.watt { year = 2013, filename = "youless-watt.lua" }
+
+ \stopluacode
+
+\stoptext
diff --git a/tex/context/modules/mkiv/x-asciimath.lua b/tex/context/modules/mkiv/x-asciimath.lua
new file mode 100644
index 000000000..e0a4a714b
--- /dev/null
+++ b/tex/context/modules/mkiv/x-asciimath.lua
@@ -0,0 +1,2209 @@
+if not modules then modules = { } end modules ['x-asciimath'] = {
+ version = 1.001,
+ comment = "companion to x-asciimath.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>Some backgrounds are discussed in <t>x-asciimath.mkiv</t>. This is a third version. I first
+tried a to make a proper expression parser but it's not that easy. First we have to avoid left
+recursion, which is not that trivial (maybe a future version of lpeg will provide that), and
+second there is not really a syntax but a mix of expressions and sequences with some fuzzy logic
+applied. Most problematic are fractions and we also need to handle incomplete expressions. So,
+instead we (sort of) tokenize the string and then do some passes over the result. Yes, it's real
+ugly and unsatisfying code mess down here. Don't take this as an example.</p>
+--ldx]]--
+
+-- todo: spaces around all elements in cleanup?
+-- todo: filter from files listed in tuc file
+
+local trace_mapping = false if trackers then trackers.register("modules.asciimath.mapping", function(v) trace_mapping = v end) end
+local trace_detail = false if trackers then trackers.register("modules.asciimath.detail", function(v) trace_detail = v end) end
+local trace_digits = false if trackers then trackers.register("modules.asciimath.digits", function(v) trace_digits = v end) end
+
+local report_asciimath = logs.reporter("mathematics","asciimath")
+
+local asciimath = { }
+local moduledata = moduledata or { }
+moduledata.asciimath = asciimath
+
+if not characters then
+ require("char-def")
+ require("char-ini")
+ require("char-ent")
+end
+
+local type, rawget = type, rawget
+local concat, insert, remove = table.concat, table.insert, table.remove
+local rep, gmatch, gsub, find = string.rep, string.gmatch, string.gsub, string.find
+local utfchar, utfbyte = utf.char, utf.byte
+
+local lpegmatch, patterns = lpeg.match, lpeg.patterns
+local S, P, R, C, V, Cc, Ct, Cs, Carg = lpeg.S, lpeg.P, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc, lpeg.Ct, lpeg.Cs, lpeg.Carg
+
+local sortedhash = table.sortedhash
+local sortedkeys = table.sortedkeys
+local formatters = string.formatters
+
+local entities = characters.entities or { }
+
+local xmltext = xml.text
+local xmlinclusion = xml.inclusion
+local xmlcollected = xml.collected
+
+-- todo: use private unicodes as temporary slots ... easier to compare
+
+local s_lparent = "\\left\\lparent"
+local s_lbrace = "\\left\\lbrace"
+local s_lbracket = "\\left\\lbracket"
+local s_langle = "\\left\\langle"
+local s_lfloor = "\\left\\lfloor"
+local s_lceil = "\\left\\lceil"
+local s_left = "\\left."
+
+local s_rparent = "\\right\\rparent"
+local s_rbrace = "\\right\\rbrace"
+local s_rbracket = "\\right\\rbracket"
+local s_rangle = "\\right\\rangle"
+local s_rfloor = "\\right\\rfloor"
+local s_rceil = "\\right\\rceil"
+local s_right = "\\right."
+
+local s_mslash = "\\middle/"
+
+local s_lbar = "\\left\\|"
+local s_mbar = "\\middle\\|"
+local s_rbar = "\\right\\|"
+
+local s_lnothing = "\\left ." -- space fools checker
+local s_rnothing = "\\right ." -- space fools checker
+
+local reserved = {
+
+ ["prod"] = { false, "\\prod" },
+ ["sinh"] = { false, "\\sinh" },
+ ["cosh"] = { false, "\\cosh" },
+ ["tanh"] = { false, "\\tanh" },
+ ["sum"] = { false, "\\sum" },
+ ["int"] = { false, "\\int" },
+ ["sin"] = { false, "\\sin" },
+ ["cos"] = { false, "\\cos" },
+ ["tan"] = { false, "\\tan" },
+ ["csc"] = { false, "\\csc" },
+ ["sec"] = { false, "\\sec" },
+ ["cot"] = { false, "\\cot" },
+ ["log"] = { false, "\\log" },
+ ["det"] = { false, "\\det" },
+ ["lim"] = { false, "\\lim" },
+ ["mod"] = { false, "\\mod" },
+ ["gcd"] = { false, "\\gcd" },
+ ["min"] = { false, "\\min" },
+ ["max"] = { false, "\\max" },
+ ["ln"] = { false, "\\ln" },
+
+ ["atan"] = { false, "\\atan" }, -- extra
+ ["acos"] = { false, "\\acos" }, -- extra
+ ["asin"] = { false, "\\asin" }, -- extra
+
+ ["arctan"] = { false, "\\arctan" }, -- extra
+ ["arccos"] = { false, "\\arccos" }, -- extra
+ ["arcsin"] = { false, "\\arcsin" }, -- extra
+
+ ["and"] = { false, "\\text{and}" },
+ ["or"] = { false, "\\text{or}" },
+ ["if"] = { false, "\\text{if}" },
+
+ ["sqrt"] = { false, "\\asciimathsqrt", "unary" },
+ ["root"] = { false, "\\asciimathroot", "binary" },
+ -- ["\\frac"] = { false, "\\frac", "binary" },
+ ["frac"] = { false, "\\frac", "binary" },
+ ["stackrel"] = { false, "\\asciimathstackrel", "binary" },
+ ["hat"] = { false, "\\widehat", "unary" },
+ ["bar"] = { false, "\\overbar", "unary" },
+ ["overbar"] = { false, "\\overbar", "unary" },
+ ["underline"] = { false, "\\underline", "unary" },
+ ["ul"] = { false, "\\underline", "unary" },
+ ["vec"] = { false, "\\overrightarrow", "unary" },
+ ["dot"] = { false, "\\dot", "unary" }, -- 0x2D9
+ ["ddot"] = { false, "\\ddot", "unary" }, -- 0xA8
+
+ -- binary operators
+
+ ["+"] = { true, "+" },
+ ["-"] = { true, "-" },
+ ["*"] = { true, "⋅" },
+ ["**"] = { true, "⋆" },
+ ["//"] = { true, "⁄" }, -- \slash
+ ["\\"] = { true, "\\" },
+ ["xx"] = { true, "×" },
+ ["times"] = { true, "×" },
+ ["-:"] = { true, "÷" },
+ ["@"] = { true, "∘" },
+ ["circ"] = { true, "∘" },
+ ["o+"] = { true, "⊕" },
+ ["ox"] = { true, "⊗" },
+ ["o."] = { true, "⊙" },
+ ["^^"] = { true, "∧" },
+ ["vv"] = { true, "∨" },
+ ["nn"] = { true, "∩" },
+ ["uu"] = { true, "∪" },
+
+ -- big operators
+
+ ["^^^"] = { true, "⋀" },
+ ["vvv"] = { true, "⋁" },
+ ["nnn"] = { true, "⋂" },
+ ["uuu"] = { true, "⋃" },
+ ["int"] = { true, "∫" },
+ ["oint"] = { true, "∮" },
+
+ -- brackets
+
+ ["("] = { true, "(" },
+ [")"] = { true, ")" },
+ ["["] = { true, "[" },
+ ["]"] = { true, "]" },
+ ["{"] = { true, "{" },
+ ["}"] = { true, "}" },
+
+ -- binary relations
+
+ ["="] = { true, "=" },
+ ["eq"] = { true, "=" },
+ ["!="] = { true, "≠" },
+ ["ne"] = { true, "≠" },
+ ["neq"] = { true, "≠" },
+ ["<"] = { true, "<" },
+ ["lt"] = { true, "<" },
+ [">"] = { true, ">" },
+ ["gt"] = { true, ">" },
+ ["<="] = { true, "≤" },
+ ["le"] = { true, "≤" },
+ ["leq"] = { true, "≤" },
+ [">="] = { true, "≥" },
+ ["ge"] = { true, "≥" },
+ ["geq"] = { true, "≥" },
+ ["-<"] = { true, "≺" },
+ [">-"] = { true, "≻" },
+ ["in"] = { true, "∈" },
+ ["!in"] = { true, "∉" },
+ ["sub"] = { true, "⊂" },
+ ["sup"] = { true, "⊃" },
+ ["sube"] = { true, "⊆" },
+ ["supe"] = { true, "⊇" },
+ ["-="] = { true, "≡" },
+ ["~="] = { true, "≅" },
+ ["~~"] = { true, "≈" },
+ ["prop"] = { true, "∝" },
+
+ -- arrows
+
+ ["rarr"] = { true, "→" },
+ ["->"] = { true, "→" },
+ ["larr"] = { true, "←" },
+ ["harr"] = { true, "↔" },
+ ["uarr"] = { true, "↑" },
+ ["darr"] = { true, "↓" },
+ ["rArr"] = { true, "⇒" },
+ ["lArr"] = { true, "⇐" },
+ ["hArr"] = { true, "⇔" },
+ ["|->"] = { true, "↦" },
+
+ -- logical
+
+ ["not"] = { true, "¬" },
+ ["=>"] = { true, "⇒" },
+ ["iff"] = { true, "⇔" },
+ ["AA"] = { true, "∀" },
+ ["EE"] = { true, "∃" },
+ ["_|_"] = { true, "⊥" },
+ ["TT"] = { true, "⊤" },
+ ["|--"] = { true, "⊢" },
+ ["|=="] = { true, "⊨" },
+
+ -- miscellaneous
+
+ ["del"] = { true, "∂" },
+ ["grad"] = { true, "∇" },
+ ["+-"] = { true, "±" },
+ ["O/"] = { true, "∅" },
+ ["oo"] = { true, "∞" },
+ ["aleph"] = { true, "ℵ" },
+ ["angle"] = { true, "∠" },
+ ["/_"] = { true, "∠" },
+ [":."] = { true, "∴" },
+ ["..."] = { true, "..." }, -- ldots
+ ["ldots"] = { true, "..." }, -- ldots
+ ["cdots"] = { true, "⋯" },
+ ["vdots"] = { true, "⋮" },
+ ["ddots"] = { true, "⋱" },
+ ["diamond"] = { true, "⋄" },
+ ["square"] = { true, "□" },
+ ["|__"] = { true, "⌊" },
+ ["__|"] = { true, "⌋" },
+ ["|~"] = { true, "⌈" },
+ ["~|"] = { true, "⌉" },
+
+ -- more
+
+ ["_="] = { true, "≡" },
+
+ -- bonus
+
+ ["prime"] = { true, "′" }, -- bonus
+ ["'"] = { true, "′" }, -- bonus
+ ["''"] = { true, "″" }, -- bonus
+ ["'''"] = { true, "‴" }, -- bonus
+
+ -- special
+
+ ["%"] = { false, "\\mathpercent" },
+ ["&"] = { false, "\\mathampersand" },
+ ["#"] = { false, "\\mathhash" },
+ ["$"] = { false, "\\mathdollar" },
+
+ -- blackboard
+
+ ["CC"] = { true, "ℂ" },
+ ["NN"] = { true, "ℕ" },
+ ["QQ"] = { true, "ℚ" },
+ ["RR"] = { true, "ℝ" },
+ ["ZZ"] = { true, "ℤ" },
+
+ -- greek lowercase
+
+ ["alpha"] = { true, "α" },
+ ["beta"] = { true, "β" },
+ ["gamma"] = { true, "γ" },
+ ["delta"] = { true, "δ" },
+ ["epsilon"] = { true, "ε" },
+ ["varepsilon"] = { true, "ɛ" },
+ ["zeta"] = { true, "ζ" },
+ ["eta"] = { true, "η" },
+ ["theta"] = { true, "θ" },
+ ["vartheta"] = { true, "ϑ" },
+ ["iota"] = { true, "ι" },
+ ["kappa"] = { true, "κ" },
+ ["lambda"] = { true, "λ" },
+ ["mu"] = { true, "μ" },
+ ["nu"] = { true, "ν" },
+ ["xi"] = { true, "ξ" },
+ ["pi"] = { true, "π" },
+ ["rho"] = { true, "ρ" },
+ ["sigma"] = { true, "σ" },
+ ["tau"] = { true, "τ" },
+ ["upsilon"] = { true, "υ" },
+ ["phi"] = { true, "φ" },
+ ["varphi"] = { true, "ϕ" },
+ ["chi"] = { true, "χ" },
+ ["psi"] = { true, "ψ" },
+ ["omega"] = { true, "ω" },
+
+ -- greek uppercase
+
+ ["Gamma"] = { true, "Γ" },
+ ["Delta"] = { true, "Δ" },
+ ["Theta"] = { true, "Θ" },
+ ["Lambda"] = { true, "Λ" },
+ ["Xi"] = { true, "Ξ" },
+ ["Pi"] = { true, "Π" },
+ ["Sigma"] = { true, "Σ" },
+ ["Phi"] = { true, "Φ" },
+ ["Psi"] = { true, "Ψ" },
+ ["Omega"] = { true, "Ω" },
+
+ -- blackboard
+
+ ["bbb a"] = { true, "𝕒" },
+ ["bbb b"] = { true, "𝕓" },
+ ["bbb c"] = { true, "𝕔" },
+ ["bbb d"] = { true, "𝕕" },
+ ["bbb e"] = { true, "𝕖" },
+ ["bbb f"] = { true, "𝕗" },
+ ["bbb g"] = { true, "𝕘" },
+ ["bbb h"] = { true, "𝕙" },
+ ["bbb i"] = { true, "𝕚" },
+ ["bbb j"] = { true, "𝕛" },
+ ["bbb k"] = { true, "𝕜" },
+ ["bbb l"] = { true, "𝕝" },
+ ["bbb m"] = { true, "𝕞" },
+ ["bbb n"] = { true, "𝕟" },
+ ["bbb o"] = { true, "𝕠" },
+ ["bbb p"] = { true, "𝕡" },
+ ["bbb q"] = { true, "𝕢" },
+ ["bbb r"] = { true, "𝕣" },
+ ["bbb s"] = { true, "𝕤" },
+ ["bbb t"] = { true, "𝕥" },
+ ["bbb u"] = { true, "𝕦" },
+ ["bbb v"] = { true, "𝕧" },
+ ["bbb w"] = { true, "𝕨" },
+ ["bbb x"] = { true, "𝕩" },
+ ["bbb y"] = { true, "𝕪" },
+ ["bbb z"] = { true, "𝕫" },
+
+ ["bbb A"] = { true, "𝔸" },
+ ["bbb B"] = { true, "𝔹" },
+ ["bbb C"] = { true, "ℂ" },
+ ["bbb D"] = { true, "𝔻" },
+ ["bbb E"] = { true, "𝔼" },
+ ["bbb F"] = { true, "𝔽" },
+ ["bbb G"] = { true, "𝔾" },
+ ["bbb H"] = { true, "ℍ" },
+ ["bbb I"] = { true, "𝕀" },
+ ["bbb J"] = { true, "𝕁" },
+ ["bbb K"] = { true, "𝕂" },
+ ["bbb L"] = { true, "𝕃" },
+ ["bbb M"] = { true, "𝕄" },
+ ["bbb N"] = { true, "ℕ" },
+ ["bbb O"] = { true, "𝕆" },
+ ["bbb P"] = { true, "ℙ" },
+ ["bbb Q"] = { true, "ℚ" },
+ ["bbb R"] = { true, "ℝ" },
+ ["bbb S"] = { true, "𝕊" },
+ ["bbb T"] = { true, "𝕋" },
+ ["bbb U"] = { true, "𝕌" },
+ ["bbb V"] = { true, "𝕍" },
+ ["bbb W"] = { true, "𝕎" },
+ ["bbb X"] = { true, "𝕏" },
+ ["bbb Y"] = { true, "𝕐" },
+ ["bbb Z"] = { true, "ℤ" },
+
+ -- fraktur
+
+ ["fr a"] = { true, "𝔞" },
+ ["fr b"] = { true, "𝔟" },
+ ["fr c"] = { true, "𝔠" },
+ ["fr d"] = { true, "𝔡" },
+ ["fr e"] = { true, "𝔢" },
+ ["fr f"] = { true, "𝔣" },
+ ["fr g"] = { true, "𝔤" },
+ ["fr h"] = { true, "𝔥" },
+ ["fr i"] = { true, "𝔦" },
+ ["fr j"] = { true, "𝔧" },
+ ["fr k"] = { true, "𝔨" },
+ ["fr l"] = { true, "𝔩" },
+ ["fr m"] = { true, "𝔪" },
+ ["fr n"] = { true, "𝔫" },
+ ["fr o"] = { true, "𝔬" },
+ ["fr p"] = { true, "𝔭" },
+ ["fr q"] = { true, "𝔮" },
+ ["fr r"] = { true, "𝔯" },
+ ["fr s"] = { true, "𝔰" },
+ ["fr t"] = { true, "𝔱" },
+ ["fr u"] = { true, "𝔲" },
+ ["fr v"] = { true, "𝔳" },
+ ["fr w"] = { true, "𝔴" },
+ ["fr x"] = { true, "𝔵" },
+ ["fr y"] = { true, "𝔶" },
+ ["fr z"] = { true, "𝔷" },
+
+ ["fr A"] = { true, "𝔄" },
+ ["fr B"] = { true, "𝔅" },
+ ["fr C"] = { true, "ℭ" },
+ ["fr D"] = { true, "𝔇" },
+ ["fr E"] = { true, "𝔈" },
+ ["fr F"] = { true, "𝔉" },
+ ["fr G"] = { true, "𝔊" },
+ ["fr H"] = { true, "ℌ" },
+ ["fr I"] = { true, "ℑ" },
+ ["fr J"] = { true, "𝔍" },
+ ["fr K"] = { true, "𝔎" },
+ ["fr L"] = { true, "𝔏" },
+ ["fr M"] = { true, "𝔐" },
+ ["fr N"] = { true, "𝔑" },
+ ["fr O"] = { true, "𝔒" },
+ ["fr P"] = { true, "𝔓" },
+ ["fr Q"] = { true, "𝔔" },
+ ["fr R"] = { true, "ℜ" },
+ ["fr S"] = { true, "𝔖" },
+ ["fr T"] = { true, "𝔗" },
+ ["fr U"] = { true, "𝔘" },
+ ["fr V"] = { true, "𝔙" },
+ ["fr W"] = { true, "𝔚" },
+ ["fr X"] = { true, "𝔛" },
+ ["fr Y"] = { true, "𝔜" },
+ ["fr Z"] = { true, "ℨ" },
+
+ -- script
+
+ ["cc a"] = { true, "𝒶" },
+ ["cc b"] = { true, "𝒷" },
+ ["cc c"] = { true, "𝒸" },
+ ["cc d"] = { true, "𝒹" },
+ ["cc e"] = { true, "ℯ" },
+ ["cc f"] = { true, "𝒻" },
+ ["cc g"] = { true, "ℊ" },
+ ["cc h"] = { true, "𝒽" },
+ ["cc i"] = { true, "𝒾" },
+ ["cc j"] = { true, "𝒿" },
+ ["cc k"] = { true, "𝓀" },
+ ["cc l"] = { true, "𝓁" },
+ ["cc m"] = { true, "𝓂" },
+ ["cc n"] = { true, "𝓃" },
+ ["cc o"] = { true, "ℴ" },
+ ["cc p"] = { true, "𝓅" },
+ ["cc q"] = { true, "𝓆" },
+ ["cc r"] = { true, "𝓇" },
+ ["cc s"] = { true, "𝓈" },
+ ["cc t"] = { true, "𝓉" },
+ ["cc u"] = { true, "𝓊" },
+ ["cc v"] = { true, "𝓋" },
+ ["cc w"] = { true, "𝓌" },
+ ["cc x"] = { true, "𝓍" },
+ ["cc y"] = { true, "𝓎" },
+ ["cc z"] = { true, "𝓏" },
+
+ ["cc A"] = { true, "𝒜" },
+ ["cc B"] = { true, "ℬ" },
+ ["cc C"] = { true, "𝒞" },
+ ["cc D"] = { true, "𝒟" },
+ ["cc E"] = { true, "ℰ" },
+ ["cc F"] = { true, "ℱ" },
+ ["cc G"] = { true, "𝒢" },
+ ["cc H"] = { true, "ℋ" },
+ ["cc I"] = { true, "ℐ" },
+ ["cc J"] = { true, "𝒥" },
+ ["cc K"] = { true, "𝒦" },
+ ["cc L"] = { true, "ℒ" },
+ ["cc M"] = { true, "ℳ" },
+ ["cc N"] = { true, "𝒩" },
+ ["cc O"] = { true, "𝒪" },
+ ["cc P"] = { true, "𝒫" },
+ ["cc Q"] = { true, "𝒬" },
+ ["cc R"] = { true, "ℛ" },
+ ["cc S"] = { true, "𝒮" },
+ ["cc T"] = { true, "𝒯" },
+ ["cc U"] = { true, "𝒰" },
+ ["cc V"] = { true, "𝒱" },
+ ["cc W"] = { true, "𝒲" },
+ ["cc X"] = { true, "𝒳" },
+ ["cc Y"] = { true, "𝒴" },
+ ["cc Z"] = { true, "𝒵" },
+
+ -- bold
+
+ ["bb a"] = { true, "𝒂" },
+ ["bb b"] = { true, "𝒃" },
+ ["bb c"] = { true, "𝒄" },
+ ["bb d"] = { true, "𝒅" },
+ ["bb e"] = { true, "𝒆" },
+ ["bb f"] = { true, "𝒇" },
+ ["bb g"] = { true, "𝒈" },
+ ["bb h"] = { true, "𝒉" },
+ ["bb i"] = { true, "𝒊" },
+ ["bb j"] = { true, "𝒋" },
+ ["bb k"] = { true, "𝒌" },
+ ["bb l"] = { true, "𝒍" },
+ ["bb m"] = { true, "𝒎" },
+ ["bb n"] = { true, "𝒏" },
+ ["bb o"] = { true, "𝒐" },
+ ["bb p"] = { true, "𝒑" },
+ ["bb q"] = { true, "𝒒" },
+ ["bb r"] = { true, "𝒓" },
+ ["bb s"] = { true, "𝒔" },
+ ["bb t"] = { true, "𝒕" },
+ ["bb u"] = { true, "𝒖" },
+ ["bb v"] = { true, "𝒗" },
+ ["bb w"] = { true, "𝒘" },
+ ["bb x"] = { true, "𝒙" },
+ ["bb y"] = { true, "𝒚" },
+ ["bb z"] = { true, "𝒛" },
+
+ ["bb A"] = { true, "𝑨" },
+ ["bb B"] = { true, "𝑩" },
+ ["bb C"] = { true, "𝑪" },
+ ["bb D"] = { true, "𝑫" },
+ ["bb E"] = { true, "𝑬" },
+ ["bb F"] = { true, "𝑭" },
+ ["bb G"] = { true, "𝑮" },
+ ["bb H"] = { true, "𝑯" },
+ ["bb I"] = { true, "𝑰" },
+ ["bb J"] = { true, "𝑱" },
+ ["bb K"] = { true, "𝑲" },
+ ["bb L"] = { true, "𝑳" },
+ ["bb M"] = { true, "𝑴" },
+ ["bb N"] = { true, "𝑵" },
+ ["bb O"] = { true, "𝑶" },
+ ["bb P"] = { true, "𝑷" },
+ ["bb Q"] = { true, "𝑸" },
+ ["bb R"] = { true, "𝑹" },
+ ["bb S"] = { true, "𝑺" },
+ ["bb T"] = { true, "𝑻" },
+ ["bb U"] = { true, "𝑼" },
+ ["bb V"] = { true, "𝑽" },
+ ["bb W"] = { true, "𝑾" },
+ ["bb X"] = { true, "𝑿" },
+ ["bb Y"] = { true, "𝒀" },
+ ["bb Z"] = { true, "𝒁" },
+
+ -- sans
+
+ ["sf a"] = { true, "𝖺" },
+ ["sf b"] = { true, "𝖻" },
+ ["sf c"] = { true, "𝖼" },
+ ["sf d"] = { true, "𝖽" },
+ ["sf e"] = { true, "𝖾" },
+ ["sf f"] = { true, "𝖿" },
+ ["sf g"] = { true, "𝗀" },
+ ["sf h"] = { true, "𝗁" },
+ ["sf i"] = { true, "𝗂" },
+ ["sf j"] = { true, "𝗃" },
+ ["sf k"] = { true, "𝗄" },
+ ["sf l"] = { true, "𝗅" },
+ ["sf m"] = { true, "𝗆" },
+ ["sf n"] = { true, "𝗇" },
+ ["sf o"] = { true, "𝗈" },
+ ["sf p"] = { true, "𝗉" },
+ ["sf q"] = { true, "𝗊" },
+ ["sf r"] = { true, "𝗋" },
+ ["sf s"] = { true, "𝗌" },
+ ["sf t"] = { true, "𝗍" },
+ ["sf u"] = { true, "𝗎" },
+ ["sf v"] = { true, "𝗏" },
+ ["sf w"] = { true, "𝗐" },
+ ["sf x"] = { true, "𝗑" },
+ ["sf y"] = { true, "𝗒" },
+ ["sf z"] = { true, "𝗓" },
+
+ ["sf A"] = { true, "𝖠" },
+ ["sf B"] = { true, "𝖡" },
+ ["sf C"] = { true, "𝖢" },
+ ["sf D"] = { true, "𝖣" },
+ ["sf E"] = { true, "𝖤" },
+ ["sf F"] = { true, "𝖥" },
+ ["sf G"] = { true, "𝖦" },
+ ["sf H"] = { true, "𝖧" },
+ ["sf I"] = { true, "𝖨" },
+ ["sf J"] = { true, "𝖩" },
+ ["sf K"] = { true, "𝖪" },
+ ["sf L"] = { true, "𝖫" },
+ ["sf M"] = { true, "𝖬" },
+ ["sf N"] = { true, "𝖭" },
+ ["sf O"] = { true, "𝖮" },
+ ["sf P"] = { true, "𝖯" },
+ ["sf Q"] = { true, "𝖰" },
+ ["sf R"] = { true, "𝖱" },
+ ["sf S"] = { true, "𝖲" },
+ ["sf T"] = { true, "𝖳" },
+ ["sf U"] = { true, "𝖴" },
+ ["sf V"] = { true, "𝖵" },
+ ["sf W"] = { true, "𝖶" },
+ ["sf X"] = { true, "𝖷" },
+ ["sf Y"] = { true, "𝖸" },
+ ["sf Z"] = { true, "𝖹" },
+
+ -- monospace
+
+ ["tt a"] = { true, "𝚊" },
+ ["tt b"] = { true, "𝚋" },
+ ["tt c"] = { true, "𝚌" },
+ ["tt d"] = { true, "𝚍" },
+ ["tt e"] = { true, "𝚎" },
+ ["tt f"] = { true, "𝚏" },
+ ["tt g"] = { true, "𝚐" },
+ ["tt h"] = { true, "𝚑" },
+ ["tt i"] = { true, "𝚒" },
+ ["tt j"] = { true, "𝚓" },
+ ["tt k"] = { true, "𝚔" },
+ ["tt l"] = { true, "𝚕" },
+ ["tt m"] = { true, "𝚖" },
+ ["tt n"] = { true, "𝚗" },
+ ["tt o"] = { true, "𝚘" },
+ ["tt p"] = { true, "𝚙" },
+ ["tt q"] = { true, "𝚚" },
+ ["tt r"] = { true, "𝚛" },
+ ["tt s"] = { true, "𝚜" },
+ ["tt t"] = { true, "𝚝" },
+ ["tt u"] = { true, "𝚞" },
+ ["tt v"] = { true, "𝚟" },
+ ["tt w"] = { true, "𝚠" },
+ ["tt x"] = { true, "𝚡" },
+ ["tt y"] = { true, "𝚢" },
+ ["tt z"] = { true, "𝚣" },
+
+ ["tt A"] = { true, "𝙰" },
+ ["tt B"] = { true, "𝙱" },
+ ["tt C"] = { true, "𝙲" },
+ ["tt D"] = { true, "𝙳" },
+ ["tt E"] = { true, "𝙴" },
+ ["tt F"] = { true, "𝙵" },
+ ["tt G"] = { true, "𝙶" },
+ ["tt H"] = { true, "𝙷" },
+ ["tt I"] = { true, "𝙸" },
+ ["tt J"] = { true, "𝙹" },
+ ["tt K"] = { true, "𝙺" },
+ ["tt L"] = { true, "𝙻" },
+ ["tt M"] = { true, "𝙼" },
+ ["tt N"] = { true, "𝙽" },
+ ["tt O"] = { true, "𝙾" },
+ ["tt P"] = { true, "𝙿" },
+ ["tt Q"] = { true, "𝚀" },
+ ["tt R"] = { true, "𝚁" },
+ ["tt S"] = { true, "𝚂" },
+ ["tt T"] = { true, "𝚃" },
+ ["tt U"] = { true, "𝚄" },
+ ["tt V"] = { true, "𝚅" },
+ ["tt W"] = { true, "𝚆" },
+ ["tt X"] = { true, "𝚇" },
+ ["tt Y"] = { true, "𝚈" },
+ ["tt Z"] = { true, "𝚉" },
+
+ -- some more undocumented
+
+ ["dx"] = { false, { "d", "x" } }, -- "{dx}" "\\left(dx\\right)"
+ ["dy"] = { false, { "d", "y" } }, -- "{dy}" "\\left(dy\\right)"
+ ["dz"] = { false, { "d", "z" } }, -- "{dz}" "\\left(dz\\right)"
+
+ -- fences
+
+ ["(:"] = { true, "(:" },
+ ["{:"] = { true, "{:" },
+ ["[:"] = { true, "[:" },
+ ["("] = { true, "(" },
+ ["["] = { true, "[" },
+ ["{"] = { true, "{" },
+ ["<<"] = { true, "⟨" }, -- why not <:
+ ["|_"] = { true, "⌊" },
+ ["|~"] = { true, "⌈" },
+ ["⟨"] = { true, "⟨" },
+ ["〈"] = { true, "⟨" },
+ ["〈"] = { true, "⟨" },
+
+ [":)"] = { true, ":)" },
+ [":}"] = { true, ":}" },
+ [":]"] = { true, ":]" },
+ [")"] = { true, ")" },
+ ["]"] = { true, "]" },
+ ["}"] = { true, "}" },
+ [">>"] = { true, "⟩" }, -- why not :>
+ ["~|"] = { true, "⌉" },
+ ["_|"] = { true, "⌋" },
+ ["⟩"] = { true, "⟩" },
+ ["〉"] = { true, "⟩" },
+ ["〉"] = { true, "⟩" },
+
+ ["lparent"] = { true, "(" },
+ ["lbracket"] = { true, "[" },
+ ["lbrace"] = { true, "{" },
+ ["langle"] = { true, "⟨" },
+ ["lfloor"] = { true, "⌊" },
+ ["lceil"] = { true, "⌈" },
+
+ ["rparent"] = { true, ")" },
+ ["rbracket"] = { true, "]" },
+ ["rbrace"] = { true, "}" },
+ ["rangle"] = { true, "⟩" },
+ ["rfloor"] = { true, "⌋" },
+ ["rceil"] = { true, "⌉" },
+
+ -- a bit special:
+
+-- ["\\frac"] = { true, "frac" },
+
+ -- now it gets real crazy, only these two:
+
+ ["&gt;"] = { true, ">" },
+ ["&lt;"] = { true, "<" },
+
+ -- extra:
+
+ -- also, invisible times
+
+ ["dd"] = { false, "{\\tf d}" },
+ ["ee"] = { false, "{\\tf e}" },
+ ["xxx"] = { true, utfchar(0x2063) }, -- invisible times
+
+}
+
+-- a..z A..Z : allemaal op italic alphabet
+-- en dan default naar upright "upr a"
+
+for k, v in next, characters.data do
+ local name = v.mathname
+ if name and not reserved[name] then
+ local char = { true, utfchar(k) }
+ reserved[ name] = char
+ -- reserved["\\" .. name] = char
+ end
+ -- local spec = v.mathspec
+ -- if spec then
+ -- for i=1,#spec do
+ -- local name = spec[i].name
+ -- if name and not reserved[name] then
+ -- reserved[name] = { true, utfchar(k) }
+ -- end
+ -- end
+ -- end
+end
+
+reserved.P = nil
+reserved.S = nil
+
+local isbinary = {
+ ["\\frac"] = true,
+ ["\\root"] = true,
+ ["\\asciimathroot"] = true,
+ ["\\asciimathstackrel"] = true,
+}
+
+local isunary = { -- can be taken from reserved
+ ["\\sqrt"] = true,
+ ["\\asciimathsqrt"] = true,
+ ["\\text"] = true, -- mathoptext
+ ["\\mathoptext"] = true, -- mathoptext
+ ["\\asciimathoptext"] = true, -- mathoptext
+ ["\\hat"] = true, -- widehat
+ ["\\widehat"] = true, -- widehat
+ ["\\bar"] = true, --
+ ["\\overbar"] = true, --
+ ["\\underline"] = true, --
+ ["\\vec"] = true, -- overrightarrow
+ ["\\overrightarrow"] = true, -- overrightarrow
+ ["\\dot"] = true, --
+ ["\\ddot"] = true, --
+
+}
+
+local isfunny = {
+ ["\\sin"] = true,
+}
+
+local isinfix = {
+ ["^"] = true,
+ ["_"] = true,
+}
+
+local isstupid = {
+ ["\\prod"] = true,
+ ["\\sinh"] = true,
+ ["\\cosh"] = true,
+ ["\\tanh"] = true,
+ ["\\sum"] = true,
+ ["\\int"] = true,
+ ["\\sin"] = true,
+ ["\\cos"] = true,
+ ["\\tan"] = true,
+ ["\\csc"] = true,
+ ["\\sec"] = true,
+ ["\\cot"] = true,
+ ["\\log"] = true,
+ ["\\det"] = true,
+ ["\\lim"] = true,
+ ["\\mod"] = true,
+ ["\\gcd"] = true,
+ ["\\min"] = true,
+ ["\\max"] = true,
+ ["\\ln"] = true,
+
+ ["\\atan"] = true,
+ ["\\acos"] = true,
+ ["\\asin"] = true,
+ true,
+ ["\\arctan"] = true,
+ ["\\arccos"] = true,
+ ["\\arcsin"] = true,
+
+ ["f"] = true,
+ ["g"] = true,
+}
+
+local isleft = {
+ [s_lparent] = true,
+ [s_lbrace] = true,
+ [s_lbracket] = true,
+ [s_langle] = true,
+ [s_lfloor] = true,
+ [s_lceil] = true,
+ [s_left] = true,
+}
+
+local isright = {
+ [s_rparent] = true,
+ [s_rbrace] = true,
+ [s_rbracket] = true,
+ [s_rangle] = true,
+ [s_rfloor] = true,
+ [s_rceil] = true,
+ [s_right] = true,
+}
+
+local issimplified = {
+}
+
+--
+
+-- special mess
+
+local d_one = R("09")
+local d_two = d_one * d_one
+local d_three = d_two * d_one
+local d_four = d_three * d_one
+local d_split = P(-1) + Carg(2) * (S(".") /"")
+
+local d_spaced = (Carg(1) * d_three)^1
+
+local digitized_1 = Cs ( (
+ d_three * d_spaced * d_split +
+ d_two * d_spaced * d_split +
+ d_one * d_spaced * d_split +
+ P(1)
+ )^1 )
+
+local p_fourbefore = d_four * d_split
+local p_fourafter = d_four * P(-1)
+
+local p_beforecomma = d_three * d_spaced^0 * d_split
+ + d_two * d_spaced^0 * d_split
+ + d_one * d_spaced^0 * d_split
+ + d_one * d_split
+
+local p_aftercomma = p_fourafter
+ + d_three * d_spaced
+ + d_two * d_spaced
+ + d_one * d_spaced
+
+local digitized_2 = Cs (
+ p_fourbefore * (p_aftercomma^0) +
+ p_beforecomma * ((p_aftercomma + d_one^1)^0)
+ )
+
+local p_fourbefore = d_four * d_split
+local p_fourafter = d_four
+local d_spaced = (Carg(1) * (d_three + d_two + d_one))^1
+local p_aftercomma = p_fourafter * P(-1)
+ + d_three * d_spaced * P(1)^0
+ + d_one^1
+
+-- local digitized_3 = Cs (
+-- p_fourbefore * p_aftercomma^0 +
+-- p_beforecomma * p_aftercomma^0
+-- )
+
+local digitized_3 = Cs((p_fourbefore + p_beforecomma) * p_aftercomma^0)
+
+local splitmethods = {
+ digitized_1,
+ digitized_2,
+ digitized_3,
+}
+
+local splitmethod = nil
+local symbolmethod = nil
+local digitseparator = utfchar(0x2008)
+local digitsymbol = "."
+
+function asciimath.setup(settings)
+ splitmethod = splitmethods[tonumber(settings.splitmethod) or 0]
+ if splitmethod then
+ digitsymbol = settings.symbol
+ if not digitsymbol or digitsymbol == "" then
+ digitsymbol = "."
+ end
+ local separator = settings.separator
+ if separator == true or not interfaces or interfaces.variables.yes then
+ digitseparator = utfchar(0x2008)
+ elseif type(separator) == "string" and separator ~= "" then
+ digitseparator = separator
+ else
+ splitmethod = nil
+ end
+ if digitsymbol ~= "." then
+ symbolmethod = lpeg.replacer(".",digitsymbol)
+ else
+ symbolmethod = nil
+ end
+ end
+end
+
+local collected_digits = { }
+local collected_filename = "asciimath-digits.lua"
+
+function numbermess(s)
+ if splitmethod then
+ local d = lpegmatch(splitmethod,s,1,digitseparator,digitsymbol)
+ if not d and symbolmethod then
+ d = lpegmatch(symbolmethod,s)
+ end
+ if d then
+ if trace_digits and s ~= d then
+ collected_digits[s] = d
+ end
+ return d
+ end
+ end
+ return s
+end
+
+-- asciimath.setup { splitmethod = 3, symbol = "," }
+-- local t = {
+-- "0.00002",
+-- "1", "12", "123", "1234", "12345", "123456", "1234567", "12345678", "123456789",
+-- "1.1",
+-- "12.12",
+-- "123.123",
+-- "1234.123",
+-- "1234.1234",
+-- "12345.1234",
+-- "1234.12345",
+-- "12345.12345",
+-- "123456.123456",
+-- "1234567.1234567",
+-- "12345678.12345678",
+-- "123456789.123456789",
+-- "0.1234",
+-- "1234.0",
+-- "1234.00",
+-- "0.123456789",
+-- "100.00005",
+-- "0.80018",
+-- "10.80018",
+-- "100.80018",
+-- "1000.80018",
+-- "10000.80018",
+-- }
+-- for i=1,#t do print(formatters["%-20s : [%s]"](t[i],numbermess(t[i]))) end
+
+statistics.register("asciimath",function()
+ if trace_digits then
+ local n = table.count(collected_digits)
+ if n > 0 then
+ table.save(collected_filename,collected_digits)
+ return string.format("%s digit conversions saved in %s",n,collected_filename)
+ else
+ os.remove(collected_filename)
+ end
+ end
+end)
+
+local p_number_base = patterns.cpnumber or patterns.cnumber or patterns.number
+local p_number = C(p_number_base)
+----- p_number = p_number_base
+local p_spaces = patterns.whitespace
+
+local p_utf_base = patterns.utf8character
+local p_utf = C(p_utf_base)
+-- local p_entity = (P("&") * C((1-P(";"))^2) * P(";"))/ entities
+
+-- entities["gt"] = ">"
+-- entities["lt"] = "<"
+-- entities["amp"] = "&"
+-- entities["dquot"] = '"'
+-- entities["quot"] = "'"
+
+local p_onechar = p_utf_base * P(-1)
+
+----- p_number = Cs((patterns.cpnumber or patterns.cnumber or patterns.number)/function(s) return (gsub(s,",","{,}")) end)
+
+local sign = P("-")^-1
+local digits = R("09")^1
+local integer = sign * digits
+local real = digits * (S(".") * digits)^-1
+local float = real * (P("E") * integer)^-1
+
+-- local number = C(float + integer)
+-- local p_number = C(float)
+local p_number = float / numbermess
+
+local k_reserved = sortedkeys(reserved)
+local k_commands = { }
+local k_unicode = { }
+
+asciimath.keys = {
+ reserved = k_reserved
+}
+
+local k_reserved_different = { }
+local k_reserved_words = { }
+
+for k, v in sortedhash(reserved) do
+ local replacement = v[2]
+ if v[1] then
+ k_unicode[k] = replacement
+ else
+ k_unicode[k] = k -- keep them ... later we remap these
+ if k ~= replacement then
+ k_reserved_different[#k_reserved_different+1] = k
+ end
+ end
+ if find(k,"^[a-zA-Z]+$") then
+ k_unicode["\\"..k] = replacement
+ else
+ k_unicode["\\"..k] = k -- dirty trick, no real unicode (still needed ?)
+ end
+ if not find(k,"[^a-zA-Z]") then
+ k_reserved_words[#k_reserved_words+1] = k
+ end
+ k_commands[k] = replacement
+end
+
+local p_reserved =
+ lpeg.utfchartabletopattern(k_reserved_different) / k_commands
+
+local p_unicode =
+-- lpeg.utfchartabletopattern(table.keys(k_unicode)) / k_unicode
+ lpeg.utfchartabletopattern(k_unicode) / k_unicode
+
+local p_texescape = patterns.texescape
+
+local function texescaped(s)
+ return lpegmatch(p_texescape,s) or s
+end
+
+local p_text =
+ P("text")
+ * p_spaces^0
+ * Cc("\\asciimathoptext")
+ * ( -- maybe balanced
+ Cs( P("{") * ((1-P("}"))^0/texescaped) * P("}") )
+ + Cs((P("(")/"{") * ((1-P(")"))^0/texescaped) * (P(")")/"}"))
+ )
+ + Cc("\\asciimathoptext") * Cs(Cc("{") * (C(patterns.undouble)/texescaped) * Cc("}"))
+
+local m_left = {
+ ["(:"] = s_langle,
+ ["{:"] = s_left,
+ ["[:"] = s_left,
+ ["("] = s_lparent,
+ ["["] = s_lbracket,
+ ["{"] = s_lbrace,
+ ["⟨"] = s_langle,
+ ["⌈"] = s_lceil,
+ ["⌊"] = s_lfloor,
+
+ -- ["<<"] = s_langle, -- why not <:
+ -- ["|_"] = s_lfloor,
+ -- ["|~"] = s_lceil,
+ -- ["〈"] = s_langle,
+ -- ["〈"] = s_langle,
+
+ -- ["lparent"] = s_lparent,
+ -- ["lbracket"] = s_lbracket,
+ -- ["lbrace"] = s_lbrace,
+ -- ["langle"] = s_langle,
+ -- ["lfloor"] = s_lfloor,
+ -- ["lceil"] = s_lceil,
+}
+
+local m_right = {
+ [":)"] = s_rangle,
+ [":}"] = s_right,
+ [":]"] = s_right,
+ [")"] = s_rparent,
+ ["]"] = s_rbracket,
+ ["}"] = s_rbrace,
+ ["⟩"] = s_rangle,
+ ["⌉"] = s_rceil,
+ ["⌋"] = s_rfloor,
+
+ -- [">>"] = s_rangle, -- why not :>
+ -- ["~|"] = s_rceil,
+ -- ["_|"] = s_rfloor,
+ -- ["〉"] = s_rangle,
+ -- ["〉"] = s_rangle,
+
+ -- ["rparent"] = s_rparent,
+ -- ["rbracket"] = s_rbracket,
+ -- ["rbrace"] = s_rbrace,
+ -- ["rangle"] = s_rangle,
+ -- ["rfloor"] = s_rfloor,
+ -- ["rceil"] = s_rceil,
+}
+
+local islimits = {
+ ["\\sum"] = true,
+ -- ["∑"] = true,
+ ["\\prod"] = true,
+ -- ["∏"] = true,
+ ["\\lim"] = true,
+}
+
+local p_left =
+ lpeg.utfchartabletopattern(m_left) / m_left
+local p_right =
+ lpeg.utfchartabletopattern(m_right) / m_right
+
+-- special cases
+
+-- local p_special =
+-- C("/")
+-- + P("\\ ") * Cc("{}") * p_spaces^0 * C(S("^_"))
+-- + P("\\ ") * Cc("\\space")
+-- + P("\\\\") * Cc("\\backslash")
+-- + P("\\") * (R("az","AZ")^1/entities)
+-- + P("|") * Cc("\\|")
+--
+-- faster bug also uglier:
+
+local p_special =
+ P("|") * Cc("\\|") -- s_mbar -- maybe always add left / right as in mml ?
+ + P("\\") * (
+ (
+ P(" ") * (
+ Cc("{}") * p_spaces^0 * C(S("^_"))
+ + Cc("\\space")
+ )
+ )
+ + P("\\") * Cc("\\backslash")
+ -- + (R("az","AZ")^1/entities)
+ + C(R("az","AZ")^1)
+ )
+
+-- open | close :: {: | :}
+
+local u_parser = Cs ( (
+ patterns.doublequoted +
+ P("text") * p_spaces^0 * P("(") * (1-P(")"))^0 * P(")") + -- -- todo: balanced
+ P("\\frac") / "frac" + -- bah
+ p_unicode +
+ p_utf_base
+)^0 )
+
+local a_parser = Ct { "tokenizer",
+ tokenizer = (
+ p_spaces
+ + p_number
+ + p_text
+ -- + Ct(p_open * V("tokenizer") * p_close) -- {: (a+b,=,1),(a+b,=,7) :}
+ -- + Ct(p_open * V("tokenizer") * p_close_right) -- { (a+b,=,1),(a+b,=,7) :}
+ -- + Ct(p_open_left * V("tokenizer") * p_right) -- {: (a+b,=,1),(a+b,=,7) }
+ + Ct(p_left * V("tokenizer") * p_right) -- { (a+b,=,1),(a+b,=,7) }
+ + p_special
+ + p_reserved
+ -- + p_utf - p_close - p_right
+ + (p_utf - p_right)
+ )^1,
+}
+
+local collapse = nil
+local serialize = table.serialize
+local f_state = formatters["level %s : %s : intermediate"]
+
+local function show_state(t,level,state)
+ report_asciimath(serialize(t,f_state(level,state)))
+end
+
+local function show_result(original,unicoded,texcoded)
+ report_asciimath("original > %s",original)
+ report_asciimath("unicoded > %s",unicoded)
+ report_asciimath("texcoded > %s",texcoded)
+end
+
+local function collapse_matrices(t)
+ local n = #t
+ if n > 4 and t[3] == "," then
+ local l1 = t[1]
+ local r1 = t[n]
+ if isleft[l1] and isright[r1] then
+ local l2 = t[2]
+ local r2 = t[n-1]
+ if type(l2) == "table" and type(r2) == "table" then
+ -- we have a matrix
+ local valid = true
+ for i=3,n-2,2 do
+ if t[i] ~= "," then
+ valid = false
+ break
+ end
+ end
+ if valid then
+ for i=2,n-1,2 do
+ local ti = t[i]
+ local tl = ti[1]
+ local tr = ti[#ti]
+ if isleft[tl] and isright[tr] then
+ -- ok
+ else
+ valid = false
+ break
+ end
+ end
+ if valid then
+ local omit = l1 == s_left and r1 == s_right
+ if omit then
+ t[1] = "\\startmatrix"
+ else
+ t[1] = l1 .. "\\startmatrix"
+ end
+ for i=2,n-1 do
+ if t[i] == "," then
+ t[i] = "\\NR"
+ else
+ local ti = t[i]
+ ti[1] = "\\NC"
+ for i=2,#ti-1 do
+ if ti[i] == "," then
+ ti[i] = "\\NC"
+ end
+ end
+ ti[#ti] = nil
+ end
+ end
+ if omit then
+ t[n] = "\\NR\\stopmatrix"
+ else
+ t[n] = "\\NR\\stopmatrix" .. r1
+ end
+ end
+ end
+ end
+ end
+ end
+ return t
+end
+
+local function collapse_bars(t)
+ local n, i, l, m = #t, 1, false, 0
+ while i <= n do
+ local current = t[i]
+ if current == "\\|" then
+ if l then
+ m = m + 1
+ t[l] = s_lbar
+ t[i] = s_rbar
+ t[m] = { unpack(t,l,i) }
+ l = false
+ else
+ l = i
+ end
+ elseif not l then
+ m = m + 1
+ t[m] = current
+ end
+ i = i + 1
+ end
+ if l then
+ local tt = { s_lnothing } -- space fools final checker
+ local tm = 1
+ for i=1,m do
+ tm = tm + 1
+ tt[tm] = t[i]
+ end
+ tm = tm + 1
+ tt[tm] = s_mbar
+ for i=l+1,n do
+ tm = tm + 1
+ tt[tm] = t[i]
+ end
+ tm = tm + 1
+ tt[tm] = s_rnothing -- space fools final checker
+ m = tm
+ t = tt
+ elseif m < n then
+ for i=n,m+1,-1 do
+ t[i] = nil
+ end
+ end
+ return t
+end
+
+local function collapse_pairs(t)
+ local n, i = #t, 1
+ while i < n do
+ local current = t[i]
+ if current == "/" and i > 1 then
+ local tl = t[i-1]
+ local tr = t[i+1]
+ local tn = t[i+2]
+ if type(tl) == "table" then
+ if isleft[tl[1]] and isright[tl[#tl]] then
+ tl[1] = "" -- todo: remove
+ tl[#tl] = nil
+ end
+ end
+ if type(tr) == "table" then
+ if tn == "^" then
+ -- brr 1/(1+x)^2
+ elseif isleft[tr[1]] and isright[tr[#tr]] then
+ tr[1] = "" -- todo: remove
+ tr[#tr] = nil
+ end
+ end
+ i = i + 2
+ elseif current == "," or current == ";" then
+ -- t[i] = current .. "\\thinspace" -- look sbad in (a,b)
+ i = i + 1
+ else
+ i = i + 1
+ end
+ end
+ return t
+end
+
+local function collapse_parentheses(t)
+ local n, i = #t, 1
+ if n > 2 then
+ while i < n do
+ local current = t[i]
+ if type(current) == "table" and isleft[t[i-1]] and isright[t[i+1]] then
+ local c = #current
+ if c > 2 and isleft[current[1]] and isright[current[c]] then
+ remove(current,c)
+ remove(current,1)
+ end
+ i = i + 3
+ else
+ i = i + 1
+ end
+ end
+ end
+ return t
+end
+
+local function collapse_stupids(t)
+ local n, m, i = #t, 0, 1
+ while i <= n do
+ m = m + 1
+ local current = t[i]
+ if isstupid[current] then
+ local one = t[i+1]
+ if type(one) == "table" then
+ one = collapse(one,level)
+ t[m] = current .. "{" .. one .. "}"
+ i = i + 2
+ else
+ t[m] = current
+ i = i + 1
+ end
+ else
+ t[m] = current
+ i = i + 1
+ end
+ end
+ if i == n then -- yes?
+ m = m + 1
+ t[m] = t[n]
+ end
+ if m < n then
+ for i=n,m+1,-1 do
+ t[i] = nil
+ end
+ end
+ return t
+end
+
+local function collapse_signs(t)
+ local n, m, i = #t, 0, 1
+ while i <= n do
+ m = m + 1
+ local current = t[i]
+ if isunary[current] then
+ local one = t[i+1]
+ if not one then
+-- m = m + 1
+ t[m] = current .. "{}" -- error
+ return t
+-- break
+ end
+ if type(one) == "table" then
+ if isleft[one[1]] and isright[one[#one]] then
+ remove(one,#one)
+ remove(one,1)
+ end
+ one = collapse(one,level)
+ elseif one == "-" and i + 2 <= n then -- or another sign ? or unary ?
+ local t2 = t[i+2]
+ if type(t2) == "string" then
+ one = one .. t2
+ i = i + 1
+ end
+ end
+ t[m] = current .. "{" .. one .. "}"
+ i = i + 2
+ elseif i + 2 <= n and isfunny[current] then
+ local one = t[i+1]
+ if isinfix[one] then
+ local two = t[i+2]
+ if two == "-" then -- or another sign ? or unary ?
+ local three = t[i+3]
+ if three then
+ if type(three) == "table" then
+ three = collapse(three,level)
+ end
+ t[m] = current .. one .. "{" .. two .. three .. "}"
+ i = i + 4
+ else
+ t[m] = current
+ i = i + 1
+ end
+ else
+ t[m] = current
+ i = i + 1
+ end
+ else
+ t[m] = current
+ i = i + 1
+ end
+ else
+ t[m] = current
+ i = i + 1
+ end
+ end
+ if i == n then -- yes?
+ m = m + 1
+ t[m] = t[n]
+ end
+ if m < n then
+ for i=n,m+1,-1 do
+ t[i] = nil
+ end
+ end
+ return t
+end
+
+local function collapse_binaries(t)
+ local n, m, i = #t, 0, 1
+ while i <= n do
+ m = m + 1
+ local current = t[i]
+ if isbinary[current] then
+ local one = t[i+1]
+ local two = t[i+2]
+ if not one then
+ t[m] = current .. "{}{}" -- error
+return t
+-- break
+ end
+ if type(one) == "table" then
+ if isleft[one[1]] and isright[one[#one]] then
+ remove(one,#one)
+ remove(one,1)
+ end
+ one = collapse(one,level)
+ end
+ if not two then
+ t[m] = current .. "{" .. one .. "}{}"
+return t
+-- break
+ end
+ if type(two) == "table" then
+ if isleft[two[1]] and isright[two[#two]] then
+ remove(two,#two)
+ remove(two,1)
+ end
+ two = collapse(two,level)
+ end
+ t[m] = current .. "{" .. one .. "}{" .. two .. "}"
+ i = i + 3
+ else
+ t[m] = current
+ i = i + 1
+ end
+ end
+ if i == n then -- yes?
+ m = m + 1
+ t[m] = t[n]
+ end
+ if m < n then
+ for i=n,m+1,-1 do
+ t[i] = nil
+ end
+ end
+ return t
+end
+
+local function collapse_infixes_1(t)
+ local n, i = #t, 1
+ while i <= n do
+ local current = t[i]
+ if isinfix[current] then
+ local what = t[i+1]
+ if what then
+ if type(what) == "table" then
+ local f, l = what[1], what[#what]
+ if isleft[f] and isright[l] then
+ remove(what,#what)
+ remove(what,1)
+ end
+ t[i+1] = collapse(what,level) -- collapse ?
+ end
+ i = i + 2
+ else
+ break
+ end
+ else
+ i = i + 1
+ end
+ end
+ return t
+end
+
+function collapse_limits(t)
+ local n, m, i = #t, 0, 1
+ while i <= n do
+ m = m + 1
+ local current = t[i]
+ if islimits[current] then
+ local one, two, first, second = nil, nil, t[i+1], t[i+3]
+ if first and isinfix[first] then
+ one = t[i+2]
+ if one then
+ -- if type(one) == "table" then
+ -- if isleft[one[1]] and isright[one[#one]] then
+ -- remove(one,#one)
+ -- remove(one,1)
+ -- end
+ -- one = collapse(one,level)
+ -- end
+ if second and isinfix[second] then
+ two = t[i+4]
+ -- if type(two) == "table" then
+ -- if isleft[two[1]] and isright[two[#two]] then
+ -- remove(two,#two)
+ -- remove(two,1)
+ -- end
+ -- two = collapse(two,level)
+ -- end
+ end
+ if two then
+ t[m] = current .. "\\limits" .. first .. "{" .. one .. "}" .. second .. "{" .. two .. "}"
+ i = i + 5
+ else
+ t[m] = current .. "\\limits" .. first .. "{" .. one .. "}"
+ i = i + 3
+ end
+ else
+ t[m] = current
+ i = i + 1
+ end
+ else
+ t[m] = current
+ i = i + 1
+ end
+ else
+ t[m] = current
+ i = i + 1
+ end
+ end
+ if i == n then -- yes?
+ m = m + 1
+ t[m] = t[n]
+ end
+ if m < n then
+ for i=n,m+1,-1 do
+ t[i] = nil
+ end
+ end
+ return t
+end
+
+local function collapse_tables(t)
+ local n, m, i = #t, 0, 1
+ while i <= n do
+ m = m + 1
+ local current = t[i]
+ if type(current) == "table" then
+ if current[1] == "\\NC" then
+ t[m] = collapse(current,level)
+ else
+ t[m] = "{" .. collapse(current,level) .. "}"
+ end
+ i = i + 1
+ else
+ t[m] = current
+ i = i + 1
+ end
+ end
+ if i == n then -- yes?
+ m = m + 1
+ t[m] = t[n]
+ end
+ if m < n then
+ for i=n,m+1,-1 do
+ t[i] = nil
+ end
+ end
+ return t
+end
+
+local function collapse_infixes_2(t)
+ local n, m, i = #t, 0, 1
+ while i < n do
+ local current = t[i]
+ if isinfix[current] and i > 1 then
+ local tl = t[i-1]
+ local tr = t[i+1]
+ local ti = t[i+2]
+ local tn = t[i+3]
+ if ti and tn and isinfix[ti] then
+ t[m] = tl .. current .. "{" .. tr .. "}" .. ti .. "{" .. tn .. "}"
+ i = i + 4
+ else
+ t[m] = tl .. current .. "{" .. tr .. "}"
+ i = i + 2
+ end
+ else
+ m = m + 1
+ t[m] = current
+ i = i + 1
+ end
+ end
+ if i == n then
+ m = m + 1
+ t[m] = t[n]
+ end
+ if m < n then
+ for i=n,m+1,-1 do
+ t[i] = nil
+ end
+ end
+ return t
+end
+
+local function collapse_fractions_1(t)
+ local n, m, i = #t, 0, 1
+ while i < n do
+ local current = t[i]
+ if current == "/" and i > 1 then
+ local tl = t[i-1]
+ local tr = t[i+1]
+ t[m] = "\\frac{" .. tl .. "}{" .. tr .. "}"
+ i = i + 2
+ if i < n then
+ m = m + 1
+ t[m] = t[i]
+ i = i + 1
+ end
+ else
+ m = m + 1
+ t[m] = current
+ i = i + 1
+ end
+ end
+ if i == n then
+ m = m + 1
+ t[m] = t[n]
+ end
+ if m < n then
+ for i=n,m+1,-1 do
+ t[i] = nil
+ end
+ end
+ return t
+end
+
+local function collapse_fractions_2(t)
+ local n, m, i = #t, 0, 1
+ while i < n do
+ local current = t[i]
+ if current == "⁄" and i > 1 then -- \slash
+ t[m] = "{" .. s_left .. t[i-1] .. s_mslash .. t[i+1] .. s_right .. "}"
+ i = i + 2
+ if i < n then
+ m = m + 1
+ t[m] = t[i]
+ i = i + 1
+ end
+ else
+ m = m + 1
+ t[m] = current
+ i = i + 1
+ end
+ end
+ if i == n then
+ m = m + 1
+ t[m] = t[n]
+ end
+ if m < n then
+ for i=n,m+1,-1 do
+ t[i] = nil
+ end
+ end
+ return t
+end
+
+local function collapse_result(t)
+ local n = #t
+ if t[1] == s_left and t[n] == s_right then -- see bar .. space needed there
+ return concat(t," ",2,n-1)
+ else
+ return concat(t," ")
+ end
+end
+
+collapse = function(t,level)
+ -- check
+ if not t then
+ return ""
+ end
+ -- tracing
+ if trace_detail then
+ if level then
+ level = level + 1
+ else
+ level = 1
+ end
+ show_state(t,level,"parsed")
+ end
+ -- steps
+ t = collapse_matrices (t) if trace_detail then show_state(t,level,"matrices") end
+ t = collapse_bars (t) if trace_detail then show_state(t,level,"bars") end
+t = collapse_stupids (t) if trace_detail then show_state(t,level,"stupids") end
+ t = collapse_pairs (t) if trace_detail then show_state(t,level,"pairs") end
+ t = collapse_parentheses(t) if trace_detail then show_state(t,level,"parentheses") end
+ t = collapse_signs (t) if trace_detail then show_state(t,level,"signs") end
+ t = collapse_binaries (t) if trace_detail then show_state(t,level,"binaries") end
+ t = collapse_infixes_1 (t) if trace_detail then show_state(t,level,"infixes (1)") end
+ t = collapse_limits (t) if trace_detail then show_state(t,level,"limits") end
+ t = collapse_tables (t) if trace_detail then show_state(t,level,"tables") end
+ t = collapse_infixes_2 (t) if trace_detail then show_state(t,level,"infixes (2)") end
+ t = collapse_fractions_1(t) if trace_detail then show_state(t,level,"fractions (1)") end
+ t = collapse_fractions_2(t) if trace_detail then show_state(t,level,"fractions (2)") end
+ -- done
+ return collapse_result(t)
+end
+
+-- todo: cache simple ones, say #str < 10, maybe weak
+
+local context = context
+local ctx_mathematics = context and context.mathematics or report_asciimath
+local ctx_type = context and context.type or function() end
+local ctx_inleft = context and context.inleft or function() end
+
+local function convert(str,totex)
+ local unicoded = lpegmatch(u_parser,str) or str
+ local texcoded = collapse(lpegmatch(a_parser,unicoded))
+ if trace_mapping then
+ show_result(str,unicoded,texcoded)
+ end
+ if totex then
+ ctx_mathematics(texcoded)
+ else
+ return texcoded
+ end
+end
+
+local n = 0
+local p = (
+ (S("{[(") + P("\\left" )) / function() n = n + 1 end
+ + (S("}])") + P("\\right")) / function() n = n - 1 end
+ + p_utf_base
+)^0
+
+local function invalidtex(str)
+ n = 0
+ lpegmatch(p,str)
+ if n == 0 then
+ return false
+ elseif n < 0 then
+ return formatters["too many left fences: %s"](-n)
+ elseif n > 0 then
+ return formatters["not enough right fences: %s"](n)
+ end
+end
+
+local collected = { }
+local indexed = { }
+
+-- bonus
+
+local p_reserved_spaced =
+ C(lpeg.utfchartabletopattern(k_reserved_words)) / " %1 "
+
+local p_text =
+ C(P("text")) / " %1 "
+ * p_spaces^0
+ * ( -- maybe balanced
+ (P("{") * (1-P("}"))^0 * P("}"))
+ + (P("(") * (1-P(")"))^0 * P(")"))
+ )
+ + patterns.doublequoted
+
+local p_expand = Cs((p_text + p_reserved_spaced + p_utf_base)^0)
+local p_compress = patterns.collapser
+
+local function cleanedup(str)
+ return lpegmatch(p_compress,lpegmatch(p_expand,str)) or str
+end
+
+-- so far
+
+local function register(s,cleanedup,collected,shortname)
+ local c = cleanedup(s)
+ local f = collected[c]
+ if f then
+ f.count = f.count + 1
+ f.files[shortname] = (f.files[shortname] or 0) + 1
+ if s ~= c then
+ f.cleanedup = f.cleanedup + 1
+ end
+ f.dirty[s] = (f.dirty[s] or 0) + 1
+ else
+ local texcoded = convert(s)
+ local message = invalidtex(texcoded)
+ if message then
+ report_asciimath("%s: %s : %s",message,s,texcoded)
+ end
+ collected[c] = {
+ count = 1,
+ files = { [shortname] = 1 },
+ texcoded = texcoded,
+ message = message,
+ cleanedup = s ~= c and 1 or 0,
+ dirty = { [s] = 1 }
+ }
+ end
+end
+
+local function wrapup(collected,indexed)
+ local n = 0
+ for k, v in sortedhash(collected) do
+ n = n + 1
+ v.n= n
+ indexed[n] = k
+ end
+end
+
+function collect(fpattern,element,collected,indexed)
+ local element = element or "am"
+ local mpattern = formatters["<%s>(.-)</%s>"](element,element)
+ local filenames = resolvers.findtexfile(fpattern)
+ if filenames and filenames ~= "" then
+ filenames = { filenames }
+ else
+ filenames = dir.glob(fpattern)
+ end
+ local cfpattern = gsub(fpattern,"^%./",lfs.currentdir())
+ local cfpattern = gsub(cfpattern,"\\","/")
+ local wildcard = string.split(cfpattern,"*")[1]
+ if not collected then
+ collected = { }
+ indexed = { }
+ end
+ for i=1,#filenames do
+ filename = gsub(filenames[i],"\\","/")
+ local splitname = (wildcard and wildcard ~= "" and string.split(filename,wildcard)[2]) or filename
+ local shortname = gsub(splitname or file.basename(filename),"^%./","")
+ if shortname == "" then
+ shortname = filename
+ end
+ local fullname = resolvers.findtexfile(filename) or filename
+ if fullname ~= "" then
+ for s in gmatch(io.loaddata(fullname),mpattern) do
+ register(s,cleanedup,collected,shortname)
+ end
+ end
+ end
+ wrapup(collected,indexed)
+ return collected, indexed
+end
+
+function filter(root,pattern,collected,indexed)
+ if not pattern or pattern == "" then
+ pattern = "am"
+ end
+ if not collected then
+ collected = { }
+ indexed = { }
+ end
+ for c in xmlcollected(root,pattern) do
+ register(xmltext(c),cleanedup,collected,xmlinclusion(c) or "" )
+ end
+ wrapup(collected,indexed)
+ return collected, indexed
+end
+
+asciimath.convert = convert
+asciimath.reserved = reserved
+asciimath.collect = collect
+asciimath.filter = filter
+asciimath.invalidtex = invalidtex
+asciimath.cleanedup = cleanedup
+
+-- sin(x) = 1 : 3.3 uncached 1.2 cached , so no real gain (better optimize the converter then)
+
+local uncrapped = {
+ ["%"] = "\\mathpercent",
+ ["&"] = "\\mathampersand",
+ ["#"] = "\\mathhash",
+ ["$"] = "\\mathdollar",
+ ["^"] = "\\Hat{\\enspace}", -- terrible hack ... tex really does it sbest to turn any ^ into a superscript
+ ["_"] = "\\underline{\\enspace}",
+}
+
+local function convert(str,nowrap)
+ if #str > 0 then
+ local unicoded = lpegmatch(u_parser,str) or str
+ if lpegmatch(p_onechar,unicoded) then
+ ctx_mathematics(uncrapped[unicoded] or unicoded)
+ else
+ local texcoded = collapse(lpegmatch(a_parser,unicoded))
+ if trace_mapping then
+ show_result(str,unicoded,texcoded)
+ end
+ if #texcoded == 0 then
+ report_asciimath("error in asciimath: %s",str)
+ else
+ local message = invalidtex(texcoded)
+ if message then
+ report_asciimath("%s: %s : %s",message,str,texcoded)
+ ctx_type(formatters["<%s>"](message))
+ elseif nowrap then
+ context(texcoded)
+ else
+ ctx_mathematics(texcoded)
+ end
+ end
+ end
+ end
+end
+
+local context = context
+
+if not context then
+
+-- trace_mapping = true
+-- trace_detail = true
+
+-- report_asciimath(cleanedup([[ac+sinx+xsqrtx+sinsqrtx+sinsqrt(x)]]))
+-- report_asciimath(cleanedup([[a "αsinsqrtx" b]]))
+-- convert([[a "αsinsqrtx" b]])
+-- report_asciimath(cleanedup([[a "α" b]]))
+-- report_asciimath(cleanedup([[//4]]))
+
+-- convert("leq\\leq")
+-- convert([[\^{1/5}log]])
+-- convert("sqrt")
+-- convert("^")
+
+-- convert[[\frac{a}{b}]]
+-- convert[[frac{a}{b}]]
+
+-- convert("frac{a}{b}")
+-- convert("\\sin{a}{b}")
+-- convert("sin{a}{b}")
+-- convert("1: rightarrow")
+-- convert("2: \\rightarrow")
+
+-- convert("((1,2,3),(4,5,6),(7,8,9))")
+
+-- convert("1/(t+x)^2")
+
+-- convert("AA a > 0 ^^ b > 0 | {:log_g:} a + {:log_g:} b")
+-- convert("AA a &gt; 0 ^^ b > 0 | {:log_g:} a + {:log_g:} b")
+
+-- convert("10000,00001")
+-- convert("4/18*100text(%)~~22,2")
+-- convert("4/18*100text(%)≈22,2")
+-- convert("62541/(197,6)≈316,05")
+
+-- convert([[sum x]])
+-- convert([[sum^(1)_(2) x]])
+-- convert([[lim_(1)^(2) x]])
+-- convert([[lim_(1) x]])
+-- convert([[lim^(2) x]])
+
+-- convert([[{: rangle]])
+-- convert([[\langle\larr]])
+-- convert([[langlelarr]])
+-- convert([[D_f=[0 ,→〉]])
+-- convert([[ac+sinx+xsqrtx]])
+-- convert([[ac+\alpha x+xsqrtx-cc b*pi**psi-3alephx / bb X]])
+-- convert([[ac+\ ^ x+xsqrtx]])
+-- convert([[d/dx(x^2+1)]])
+-- convert([[a "αsinsqrtx" b]])
+-- convert([[a "α" b]])
+-- convert([[//4]])
+-- convert([[ {(a+b,=,1),(a+b,=,7)) ]])
+
+-- convert([[ 2/a // 5/b = (2 b) / ( a b) // ( 5 a ) / ( a b ) = (2 b ) / ( 5 a ) ]])
+-- convert([[ (2+x)/a // 5/b ]])
+
+-- convert([[ ( 2/a ) // ( 5/b ) = ( (2 b) / ( a b) ) // ( ( 5 a ) / ( a b ) ) = (2 b ) / ( 5 a ) ]])
+
+-- convert([[ (x/y)^3 = x^3/y^3 ]])
+
+-- convert([[ {: (1,2) :} ]])
+-- convert([[ {: (a+b,=,1),(a+b,=,7) :} ]])
+-- convert([[ { (a+b,=,1),(a+b,=,7) :} ]])
+-- convert([[ {: (a+b,=,1),(a+b,=,7) } ]])
+-- convert([[ { (a+b,=,1),(a+b,=,7) } ]])
+
+-- convert([[(1,5 ±sqrt(1,25 ),0 )]])
+-- convert([[1//2]])
+-- convert([[(p)/sqrt(p)]])
+-- convert([[u_tot]])
+-- convert([[u_tot=4,4 L+0,054 T]])
+
+-- convert([[ [←;0,2] ]])
+-- convert([[ [←;0,2⟩ ]])
+-- convert([[ ⟨←;0,2 ) ]])
+-- convert([[ ⟨←;0,2 ] ]])
+-- convert([[ ⟨←;0,2⟩ ]])
+
+-- convert([[ x^2(x-1/16)=0 ]])
+-- convert([[ y = ax + 3 - 3a ]])
+-- convert([[ y= ((1/4)) ^x ]])
+-- convert([[ x=\ ^ (1/4) log(0 ,002 )= log(0,002) / (log(1/4) ]])
+-- convert([[ x=\ ^glog(y) ]])
+-- convert([[ x^ (-1 1/2) =1/x^ (1 1/2)=1/ (x^1*x^ (1/2)) =1/ (xsqrt(x)) ]])
+-- convert([[ x^2(10 -x)&gt;2 x^2 ]])
+-- convert([[ x^4&gt;x ]])
+
+ return
+
+end
+
+interfaces.implement {
+ name = "asciimath",
+ actions = convert,
+ arguments = "string"
+}
+
+interfaces.implement {
+ name = "justasciimath",
+ actions = convert,
+ arguments = { "string", true },
+}
+
+local ctx_typebuffer = context.typebuffer
+local ctx_mathematics = context.mathematics
+local ctx_color = context.color
+
+local sequenced = table.sequenced
+local assign_buffer = buffers.assign
+
+local show = { }
+asciimath.show = show
+
+local collected, indexed, ignored = { }, { }, { }
+
+local color = { "darkred" }
+
+function show.ignore(n)
+ if type(n) == "string" then
+ local c = collected[n]
+ n = c and c.n
+ end
+ if n then
+ ignored[n] = true
+ end
+end
+
+function show.count(n,showcleanedup)
+ local v = collected[indexed[n]]
+ local count = v.count
+ local cleanedup = v.cleanedup
+ if not showcleanedup or cleanedup == 0 then
+ context(count)
+ elseif count == cleanedup then
+ ctx_color(color,count)
+ else
+ context("%s+",count-cleanedup)
+ ctx_color(color,cleanedup)
+ end
+end
+
+local h = { }
+local am = { "am" }
+
+function show.nofdirty(n)
+ local k = indexed[n]
+ local v = collected[k]
+ local n = v.cleanedup
+ h = { }
+ if n > 0 then
+ for d, n in sortedhash(v.dirty) do
+ if d ~= k then
+ h[#h+1] = { d, n }
+ end
+ end
+ end
+ context(#h)
+end
+
+function show.dirty(m,wrapped)
+ local d = h[m]
+ if d then
+ ctx_inleft(d[2])
+ if wrapped then
+ assign_buffer("am",'"' .. d[1] .. '"')
+ else
+ assign_buffer("am",d[1])
+ end
+ ctx_typebuffer(am)
+ end
+end
+
+function show.files(n)
+ context(sequenced(collected[indexed[n]].files," "))
+end
+
+function show.input(n,wrapped)
+ if wrapped then
+ assign_buffer("am",'"' .. indexed[n] .. '"')
+ else
+ assign_buffer("am",indexed[n])
+ end
+ ctx_typebuffer(am)
+end
+
+function show.result(n)
+ local v = collected[indexed[n]]
+ if ignored[n] then
+ context("ignored")
+ elseif v.message then
+ ctx_color(color, v.message)
+ else
+ ctx_mathematics(v.texcoded)
+ end
+end
+
+function show.load(str,element)
+ collected, indexed, ignored = { }, { }, { }
+ local t = utilities.parsers.settings_to_array(str)
+ for i=1,#t do
+ asciimath.collect(t[i],element or "am",collected,indexed)
+ end
+end
+
+function show.filter(id,element)
+ collected, indexed, ignored = { }, { }, { }
+ asciimath.filter(lxml.getid(id),element or "am",collected,indexed)
+end
+
+function show.max()
+ context(#indexed)
+end
+
+function show.statistics()
+ local usedfiles = { }
+ local noffiles = 0
+ local nofokay = 0
+ local nofbad = 0
+ local nofcleanedup = 0
+ for k, v in next, collected do
+ if ignored[v.n] then
+ nofbad = nofbad + v.count
+ elseif v.message then
+ nofbad = nofbad + v.count
+ else
+ nofokay = nofokay + v.count
+ end
+ nofcleanedup = nofcleanedup + v.cleanedup
+ for k, v in next, v.files do
+ local u = usedfiles[k]
+ if u then
+ usedfiles[k] = u + 1
+ else
+ noffiles = noffiles + 1
+ usedfiles[k] = 1
+ end
+ end
+ end
+ local NC = context.NC
+ local NR = context.NR
+ local EQ = context.EQ
+ context.starttabulate { "|B||" }
+ NC() context("files") EQ() context(noffiles) NC() NR()
+ NC() context("formulas") EQ() context(nofokay+nofbad) NC() NR()
+ NC() context("uniques") EQ() context(#indexed) NC() NR()
+ NC() context("cleanedup") EQ() context(nofcleanedup) NC() NR()
+ NC() context("errors") EQ() context(nofbad) NC() NR()
+ context.stoptabulate()
+end
+
+function show.save(name)
+ table.save(name ~= "" and name or "dummy.lua",collected)
+end
diff --git a/tex/context/modules/mkiv/x-asciimath.mkiv b/tex/context/modules/mkiv/x-asciimath.mkiv
new file mode 100644
index 000000000..d3a629c81
--- /dev/null
+++ b/tex/context/modules/mkiv/x-asciimath.mkiv
@@ -0,0 +1,436 @@
+%D \module
+%D [ file=x-asciimath,
+%D version=2014.06.01, % 2006.04.24, % 1999.11.06,
+%D title=\CONTEXT\ Modules,
+%D subtitle=AsciiMath,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{x-asciimath}{}
+
+%D When the Math4All project started, we immediately started using content \MATHML.
+%D Because in school math there is often a reference to calculator input, we also
+%D provided what we called \quote {calcmath}: a predictable expression based way
+%D entering math. At some point \OPENMATH\ was also used but that was later
+%D abandoned because editing is more cumbersome.
+%D
+%D Due to limitations in the web variant (which is independent of rendering for
+%D paper but often determines the coding of document, not seldom for the worse) the
+%D switch was made to presentational \MATHML. But even that proved to be too complex
+%D for rendering on the web, so it got converted to so called \ASCIIMATH\ which
+%D can be rendered using some \JAVASCRIPT\ magic. However, all the formulas (and
+%D we're talking of tens of thousands of them) were very precisely coded by the main
+%D author. Because in intermediate stages of the editing (by additional authors) a
+%D mixture of \MATHML\ and \ASCIIMATH\ was used, we wrote the first version of this
+%D module. As reference we took \url
+%D {http://www1.chapman.edu/~jipsen/mathml/asciimath.html} and. The idea was to
+%D stick to \MATHML\ as reference and if needed use \ASCIIMATH\ as annotation.
+%D
+%D Eventually we ended up with supporting several math encodings in \CONTEXT\ that
+%D could be used mixed: content \MATHML\ (preferred), presentation \MATHML\ (often
+%D messy), \OPENMATH\ (somewhat minimalistic) calcmath (handy for students who are
+%D accustomed to calculators), \ASCIIMATH\ (to make web support easier) and of
+%D course \TEX.
+%D
+%D The first version had some limitations as we didn't want to support all quirks of
+%D \ASCIIMATH\ and also because I was not really in the mood to write a complex parser
+%D when a bit of sane coding can work equally well. Some comments from that version:
+%D
+%D \startnarrower
+%D \startitemize
+%D \item We support only the syntactically clear variants and as long as lpeg does
+%D not support left recursion this is as far as we want to go.
+%D \item The parser is rather insensitive for spaces but yet the advice is to avoid
+%D weird coding like \type {d/dxf(x)} but use \type {d/dx f(x)} instead. After
+%D all we're not in a compact coding cq.\ parser challenge.
+%D \item We also don't support the somewhat confusing \type {sqrt sqrt 2} nor \type
+%D {root3x} (although the second one kind of works). A bit of defensive coding
+%D does not hurt.
+%D \item We can process \type {a/b/c/d} but it's not compatible with the default
+%D behaviour of \ASCIIMATH. Use grouping instead. Yes, we do support the somewhat
+%D nonstandard grouping token mix.
+%D \item You should use explicit \type {text(..)} directives as one can never be sure
+%D what is a reserved word and not.
+%D \stopitemize
+%D
+%D Actually, as the only parsing sensitive elements of \TEX\ are fractions (\type {\over}
+%D and friends, a restricted use of \TEX\ coding is probably as comprehensive and
+%D parsable. The webpage with examples served as starting point so anything beyond
+%D what can be found there isn't supported.
+%D \stopnarrower
+%D
+%D Then in 2014 something bad happened. Following the fashion of minimal encoding
+%D (which of course means messy encoding of complex cases and which can make authors
+%D sloppy too) the web based support workflow of the mentioned project ran into some
+%D limitations and magically one day all carefully coded \MATHML\ was converted into
+%D \ASCIIMATH. As there was no way to recover the original thousands of files and
+%D tens of thousands of formulas we were suddenly stuck with \ASCIIMATH. Because the
+%D conversion had be done automagically, we also saw numerous errors and were forced
+%D to come up with some methods to check formulas. Because \MATHML\ poses some
+%D restrictions it has predictable rendering; \ASCIIMATH\ on the other hand enforces
+%D no structure. Also, because \MATHML\ has to be valid \XML\ it always processes.
+%D Of course, during the decade that the project had run we also had to built in
+%D some catches for abuse but at least we had a relatively stable and configurable
+%D subsystem. So, in order to deal with less predictable cases as well as extensive
+%D checking, a new \ASCIIMATH\ parser was written, one that could also be used to
+%D trace bad coding.
+%D
+%D Because the formal description is incomplete, and because some links to resources
+%D are broken, and because some testing on the web showed that sequences of characters
+%D are interpreted that were not mentioned anywhere (visible), and because we noticed
+%D that the parser was dangerously tolerant, the new code is quite different from the
+%D old code.
+%D
+%D One need to keep in mind that because spaces are optional, the only robust way to
+%D edit \ASCIIMATH\ is to use a \WYSIWYG\ editor and hope that the parser doesn't
+%D change ever. Keys are picked up from spaceless sequences and when not recognized
+%D a (sequence) of characters is considered to be variables. So, \type {xsqrtx} is
+%D valid and renders as \type {$x\sqrt{x}$}, \type {xx} becomes \type {×} (times)
+%D but \type {ac} becomes \type {$a c$} (a times c). We're lucky that \type {AC} is
+%D not turned into Alternating Current, but who knows what happens a few years from
+%D now. So, we do support this spaceless mess, but users are warned: best use a
+%D spacy sequence. The extra amount of spaces (at one byte each) an author has to
+%D include in his|/|her active writing time probably stays below the size of one
+%D holiday picture. Another complication is that numbers (in Dutch) use commas instead
+%D of periods, but vectors use commas as well. We also hav esome different names for
+%D functions which then can conflict with the expectations about collapsed variables.
+%D
+%D It must be noted that simplified encodings (that seem to be the fashion today)
+%D can demand from applications to apply fuzzy logic to make something work out
+%D well. Because we have sequential data that gets rendered, sometimes wrong input
+%D gets obscured simply by the rendering: like the comma's in numbers as well as
+%D for separators (depending on space usage), or plain wrong symbols that somehow
+%D get a representation anyway. This in itself is more a side effect of trying to
+%D use the simplified encoding without applying rules (in the input) or to use it
+%D beyong its intended usage, which then of course can lead to adapted parsers and
+%D catches that themselves trigger further abuse. Imagine that instead of developing
+%D new cars, planes, space ships, mobile phones, computers we would have adapted
+%D horse cars, kites, firework, old fashioned phones and mechanical calculators in a
+%D similar way: patch upon patch of traditional means for sure would not have
+%D worked. So, when you use \ASCIIMATH\ best check immediately how it gets rendered
+%D in the browser as well as on paper. And be prepared to check the more complex
+%D code in the future again. We don't offer any guarantees but of course will try to
+%D keep up.
+%D
+%D In retrospect I sometimes wonder if the energy put into constantly adapting to
+%D the fashion of the day pays off. Probably not. It definitely doesn't pay of.
+%D
+%D More complex crap:
+%D
+%D 1: $x + \stackrel{comment}{\stackrel{\utfchar{"23DE}}{yyyyyyyy}} = y$ \blank
+%D 2: \asciimath{x + stackrel{\utfchar{"23DE}}{yyyyyyyy} = y} \blank
+%D 3: \asciimath{x + stackrel{yyyyyyyy}{\utfchar{"23DE}} = y} \blank
+%D 4: \asciimath{x + stackrel{"comment"}{stackrel{\utfchar{"23DE}}{yyyyyyyy}} = y} \blank
+
+\usemodule[mathml-basics]
+
+\startmodule[asciimath]
+
+\unprotect
+
+\writestatus{asciimath}{beware, this is an experimental (m4all only) module}
+
+%D Hacks:
+
+\unexpanded\def\asciimathoptext #1{\ifmmode\mathoptext{#1}\else#1\fi}
+\unexpanded\def\asciimathoptexttraced #1{\ifmmode\mathoptext{\color[darkgreen]{#1}}\else\color[darkgreen]{#1}\fi}
+\unexpanded\def\asciimathstackrel #1#2{\mathematics{\mathop{\let\limits\relax\mover{#2}{#1}}}}
+\unexpanded\def\asciimathroot #1#2{\sqrt[#1]{#2}}
+\unexpanded\def\asciimathsqrt #1{\sqrt{#1}}
+
+%D The core commands:
+
+% if we need to set
+
+\installsetuponlycommandhandler {asciimath} {asciimath}
+
+\appendtoks
+ \ctxlua{moduledata.asciimath.setup {
+ splitmethod = "\asciimathparameter\c!splitmethod",
+ separator = "\asciimathparameter\c!separator",
+ symbol = "\asciimathparameter\c!symbol",
+ }}%
+\to \everysetupasciimath
+
+\newtoks\everyasciimath
+
+% \appendtoks
+% \ignorediscretionaries
+% \to \everyasciimath
+
+\appendtoks
+ \enableautofences
+\to \everyasciimath
+
+\unexpanded\def\asciimath
+ {\doifnextoptionalelse\asciimath_yes\asciimath_nop}
+
+\def\asciimath_yes[#1]#2%
+ {\mathematics
+ [#1]%
+ {\the\everyasciimath%
+ \clf_justasciimath{\detokenize\expandafter{\normalexpanded{#2}}}}}
+
+\def\asciimath_nop#1%
+ {\mathematics
+ {\the\everyasciimath
+ \clf_justasciimath{\detokenize\expandafter{\normalexpanded{#1}}}}}
+
+\unexpanded\def\ctxmoduleasciimath#1%
+ {\ctxlua{moduledata.asciimath.#1}}
+
+%D Some tracing commands. Using tex commands is 10\% slower that directly piping
+%D from \LUA, but this is non|-|critical code.
+
+\unexpanded\def\ShowAsciiMathLoad [#1]{\ctxlua{moduledata.asciimath.show.load("#1")}}
+\unexpanded\def\ShowAsciiMathIgnore[#1]{\ctxlua{moduledata.asciimath.show.ignore("#1")}}
+\unexpanded\def\ShowAsciiMathXML #1#2{\ctxlua{moduledata.asciimath.show.filter("#1","#2")}}
+\unexpanded\def\ShowAsciiMathStats {\ctxlua{moduledata.asciimath.show.statistics()}}
+\unexpanded\def\ShowAsciiMathMax {\ctxlua{moduledata.asciimath.show.max()}}
+
+\unexpanded\def\ShowAsciiMathResult#1%
+ {\begingroup
+ \blank
+ % if we are in vmode, we don't get positions i.e. a smaller tuc file
+ \inleft{\ttbf#1\hfill\ctxlua{moduledata.asciimath.show.count(#1,true)}}%
+ \dontleavehmode
+ \begingroup
+ \ttbf
+ \ctxlua{moduledata.asciimath.show.files(#1)}
+ \endgroup
+ \blank[medium,samepage]
+ \startcolor[darkblue]
+ \ctxlua{moduledata.asciimath.show.input(#1,true)}
+ \stopcolor
+ \blank[medium,samepage]
+ \doifmode{asciimath:show:dirty} {
+ \dorecurse{\ctxlua{moduledata.asciimath.show.nofdirty(#1)}} {
+ \ctxlua{moduledata.asciimath.show.dirty(\recurselevel,true)}
+ \blank[medium,samepage]
+ }
+ }
+ \ctxlua{moduledata.asciimath.show.result(#1)}
+ \blank
+ \endgroup}
+
+\unexpanded\def\ShowAsciiMathStart
+ {\begingroup
+ \let\asciimathoptext\asciimathoptexttraced
+ \setuptyping[\v!buffer][\c!before=,\c!after=]
+ \setupmargindata[\v!left][\c!style=]}
+
+\unexpanded\def\ShowAsciiMathStop
+ {\endgroup}
+
+\unexpanded\def\ShowAsciiMath
+ {\dodoubleempty\doShowAsciiMath}
+
+\unexpanded\def\doShowAsciiMath[#1][#2]%
+ {\iffirstargument
+ \ShowAsciiMathStart
+ \ShowAsciiMathLoad[#1]
+ \ifsecondargument
+ \ShowAsciiMathIgnore[#2]
+ \fi
+ \dorecurse{\ShowAsciiMathMax}{\ShowAsciiMathResult\recurselevel}
+ \page
+ \ShowAsciiMathStats
+ \ShowAsciiMathStop
+ \fi}
+
+\unexpanded\def\xmlShowAsciiMath#1#2%
+ {\iffirstargument
+ \ShowAsciiMathStart
+ \ShowAsciiMathXML{#1}{#2}%
+ \dorecurse{\ShowAsciiMathMax}{\ShowAsciiMathResult\recurselevel}
+ \page
+ \ShowAsciiMathStats
+ \ShowAsciiMathStop
+ \fi}
+
+\unexpanded\def\ShowAsciiMathSave
+ {\dosingleempty\doShowAsciiMathSave}
+
+\unexpanded\def\doShowAsciiMathSave[#1]%
+ {\ctxlua{moduledata.asciimath.show.save("#1")}}
+
+\protect
+
+\startsetups asciimath:layout
+
+ \setupbodyfont
+ % [pagella,10pt]
+ [dejavu,10pt]
+
+ \setuplayout
+ [backspace=35mm,
+ leftmargin=20mm,
+ rightmargindistance=0pt,
+ leftmargindistance=5mm,
+ cutspace=1cm,
+ topspace=1cm,
+ bottomspace=1cm,
+ width=middle,
+ height=middle,
+ header=0cm,
+ footer=1cm]
+
+ \setupheadertexts
+ []
+
+ \setupfootertexts
+ [\currentdate][\pagenumber]
+
+ \setupalign
+ [flushleft,verytolerant,stretch]
+
+ \dontcomplain
+
+\stopsetups
+
+\stopmodule
+
+\continueifinputfile{x-asciimath.mkiv}
+
+%D This will become an extra.
+
+\showframe
+
+\setups[asciimath:layout]
+
+% \enabletrackers[modules.asciimath.mapping]
+% \enabletrackers[modules.asciimath.detail]
+
+% \starttext
+% \enablemode[asciimath:show:dirty]
+% \ShowAsciiMath[e:/temporary/asciimath/*.xml]
+% % \ShowAsciiMathSave[e:/temporary/asciimath/asciimath.lua]
+% \stoptext
+
+\starttext
+\unexpanded\def\MyAsciiMath#1{\startformula\asciimath{#1}\stopformula}
+\startlines
+\MyAsciiMath{x^2 / 10 // z_12^34 / 20}
+% \MyAsciiMath{{:{:x^2:} / 10:} // {:{:z_12^34 :} / 20:}}
+% \MyAsciiMath{x^2+y_1+z_12^34}
+% \MyAsciiMath{sin^-1(x)}
+% \MyAsciiMath{d/dx f(x)=lim_(h->0) (f(x+h)-f(x))/h}
+% \MyAsciiMath{f(x)=sum_(n=0)^oo(f^((n))(a))/(n!)(x-a)^n}
+% \MyAsciiMath{int_0^1 f(x)dx}
+% \MyAsciiMath{int^1_0 f(x)dx}
+% \MyAsciiMath{a//b}
+% \MyAsciiMath{a//\alpha}
+% \MyAsciiMath{(a/b)/(d/c)}
+% \MyAsciiMath{((a*b))/(d/c)}
+% \MyAsciiMath{[[a,b],[c,d]]((n),(k))}
+% \MyAsciiMath{1/x={(1,text{if } x!=0),(text{undefined},if x=0):}}
+% \MyAsciiMath{{ (1,2), (x,(x + text(x))) }}
+% \MyAsciiMath{{(1,2),(x,(x+text(x))),(x,text(x))}}
+% \MyAsciiMath{{(1,2),(x,(x+text(x))),(x,x text(x))}}
+% \MyAsciiMath{{(1,2/2),(x,(x+x^22+sqrt(xx))),(x,x text(xyz))}}
+% \MyAsciiMath{{(1,2/2),(x,(x+x^22+sqrt(xx))),(x,text(xyz)+1+text(hans))}}
+% \MyAsciiMath{<<a,b>> text{and} {:(x,y),(u,v):}}
+% \MyAsciiMath{(a,b] = {x text(in) RR | a < x <= b}}
+% \MyAsciiMath{a/b / c/d = (a * d) / (b * d) / (b * c) / (b * d) = (a * d) / (b * c)}
+% \MyAsciiMath{ (a/b) // (c/d) = ( (a * d) / (b * d) ) // ( (b * c) / (b * d) ) = (a * d) / (b * c)}
+% \MyAsciiMath{sin(x+1)_3^2/b / c/d}
+% \MyAsciiMath{{:{:sin(x+1)_3^2:}/b:} / {:c/d:}}
+% \MyAsciiMath{cos(a) + sin(x+1)_3^2/b / c/d = (a * d) / (b * d) / (b * c) / (b * d) = (a * d) / (b * c)}
+% \MyAsciiMath{S_(11)}
+% \MyAsciiMath{f(x)}
+% \MyAsciiMath{sin(x)}
+% \MyAsciiMath{sin(x+1)}
+% \MyAsciiMath{sin^-1(x)}
+% \MyAsciiMath{sin(2x)}
+% \MyAsciiMath{a_2^2}
+% \MyAsciiMath{( (S_(11),S_(12),S_(1n)),(vdots,ddots,vdots),(S_(m1),S_(m2),S_(mn)) ]}
+% \MyAsciiMath{frac a b}
+% \MyAsciiMath{sin(x)/2 // cos(x)/pi}
+% \MyAsciiMath{a/13 // c/d}
+% \MyAsciiMath{a/b // c/d}
+% \MyAsciiMath{x}
+% \MyAsciiMath{x^2}
+% \MyAsciiMath{sqrt x}
+% \MyAsciiMath{sqrt (x)}
+% \MyAsciiMath{root 2 x}
+% \MyAsciiMath{x+x}
+% \MyAsciiMath{x/3}
+% \MyAsciiMath{x^2 / 10}
+% \MyAsciiMath{x^2 / 10 // z_12^34 / 20}
+% \MyAsciiMath{a^23}
+% \MyAsciiMath{a^{:b^23:}+3x}
+% \MyAsciiMath{a/b / c/d}
+% \MyAsciiMath{sin(x)/b / c/d}
+% \MyAsciiMath{sin(x)/b // c/d}
+% \MyAsciiMath{a/b / c/d = (a * d) / (b * d) / (b * c) / (b * d) = (a * d) / (b * c) }
+% \MyAsciiMath{{:{:x^2:} / 10:} // {:{:z_12^34 :} / 20:}}
+% \MyAsciiMath{x^2+y_1+z_12^34}
+% \MyAsciiMath{sin^-1(x)}
+% \MyAsciiMath{d/dx f(x)=lim_(h->0) (f(x+h)-f(x))/h}
+% \MyAsciiMath{f(x)=sum_(n=0)^oo(f^((n))(a))/(n!)(x-a)^n}
+% \MyAsciiMath{int_0^1 f(x)dx}
+% \MyAsciiMath{int^1_0 f(x)dx}
+% \MyAsciiMath{2x}
+% \MyAsciiMath{a//b}
+% \MyAsciiMath{a//\alpha}
+% \MyAsciiMath{(a/b)/(d/c)}
+% \MyAsciiMath{((a*b))/(d/c)}
+% \MyAsciiMath{[[a,b],[c,d]]((n),(k))}
+% \MyAsciiMath{1/x={(1,text{if } x!=0),(text{undefined},if x=0):}}
+% \MyAsciiMath{{ (1,2), (x,(x + text(x))) }}
+% \MyAsciiMath{{(1,2),(x,(x+text(x))),(x,text(x))}}
+% \MyAsciiMath{{(1,2),(x,(x+text(x))),(x,x text(x))}}
+% \MyAsciiMath{{(1,2/2),(x,(x+x^22+sqrt(xx))),(x,x text(xyz))}}
+% \MyAsciiMath{{(1,2/2),(x,(x+x^22+sqrt(xx))),(x,text(xyz)+1+text(hans))}}
+% \MyAsciiMath{<<a,b>> text{and} {:(x,y),(u,v):}}
+% \MyAsciiMath{(a,b] = {x text(in) RR | a < x <= b}}
+% \MyAsciiMath{x^-2}
+% \MyAsciiMath{x^2(x-1/16)=0}
+% \MyAsciiMath{y= ((1/4)) ^x}
+% \MyAsciiMath{log (0,002) / (log(1/4))}
+% \MyAsciiMath{x=ax+b \ oeps}
+% \MyAsciiMath{x=\ ^ (1/4) log(x)}
+% \MyAsciiMath{x=\ ^ (1/4) log(0 ,002 )= log(0,002) / (log(1/4))}
+% \MyAsciiMath{x^ (-1 1/2) =1/x^ (1 1/2)=1/ (x^1*x^ (1/2)) =1/ (xsqrt(x))}
+% \MyAsciiMath{x^2(10 -x)&gt;2 x^2}
+% \MyAsciiMath{x^4&gt;x}
+\stoplines
+
+\setupasciimath[splitmethod=3,symbol={{,}}]
+
+\startlines
+\asciimath{sqrt 1}
+\asciimath{sqrt 1.2}
+\asciimath{sqrt 1.2}
+\asciimath{1}
+\asciimath{12}
+\asciimath{123}
+\asciimath{1234}
+\asciimath{12345}
+\asciimath{123456}
+\asciimath{1234567}
+\asciimath{12345678}
+\asciimath{123456789}
+\asciimath{1.1}
+\asciimath{12.12}
+\asciimath{1234.123}
+\asciimath{1234.1234}
+\asciimath{12345.1234}
+\asciimath{1234.12345}
+\asciimath{12345.12345}
+\asciimath{123456.123456}
+\asciimath{1234567.1234567}
+\asciimath{12345678.12345678}
+\asciimath{123456789.123456789}
+\asciimath{0.1234}
+\asciimath{1234.0}
+\asciimath{1234.00}
+\asciimath{0.123456789}
+\stoplines
+
+\stoptext
diff --git a/tex/context/modules/mkiv/x-calcmath.lua b/tex/context/modules/mkiv/x-calcmath.lua
new file mode 100644
index 000000000..c96d8d0ac
--- /dev/null
+++ b/tex/context/modules/mkiv/x-calcmath.lua
@@ -0,0 +1,363 @@
+if not modules then modules = { } end modules ['x-calcmath'] = {
+ version = 1.001,
+ comment = "companion to x-calcmath.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- this really needs to be redone
+
+local format, lower, upper, gsub, sub = string.format, string.lower, string.upper, string.gsub, string.sub
+local concat = table.concat
+local lpegmatch = lpeg.match
+
+local calcmath = { }
+local moduledata = moduledata or { }
+moduledata.calcmath = calcmath
+
+local context = context
+
+local list_1 = {
+ "median", "min", "max", "round", "ln", "log",
+ "sin", "cos", "tan", "sinh", "cosh", "tanh"
+}
+local list_2 = {
+ "int", "sum", "prod"
+}
+local list_3 = {
+ "f", "g"
+}
+local list_4 = {
+ "pi", "inf"
+}
+
+local list_1_1 = { }
+local list_2_1 = { }
+local list_2_2 = { }
+local list_2_3 = { }
+local list_4_1 = { }
+
+local frozen = false
+
+local function freeze()
+ for k=1,#list_1 do
+ local v = list_1[k]
+ list_1_1[v] = "\\".. upper(v) .." "
+ end
+ for k=1,#list_2 do
+ local v = list_2[k]
+ list_2_1[v .. "%((.-),(.-),(.-)%)"] = "\\" .. upper(v) .. "^{%1}_{%2}{%3}"
+ list_2_2[v .. "%((.-),(.-)%)"] = "\\" .. upper(v) .. "^{%1}{%2}"
+ list_2_3[v .. "%((.-)%)"] = "\\" .. upper(v) .. "{%1}"
+ end
+ for k=1,#list_4 do
+ local v = list_4[k]
+ list_4_1[v] = "\\" .. upper(v)
+ end
+ frozen = true
+end
+
+local entities = {
+ ['gt'] = '>',
+ ['lt'] = '<',
+}
+
+local symbols = {
+ ["<="] = "\\LE ",
+ [">="] = "\\GE ",
+ ["=<"] = "\\LE ",
+ ["=>"] = "\\GE ",
+ ["=="] = "\\EQ ",
+ ["<" ] = "\\LT ",
+ [">" ] = "\\GT ",
+ ["="] = "\\EQ ",
+}
+
+local function nsub(str,tag,pre,post)
+ return (gsub(str,tag .. "(%b())", function(body)
+ return pre .. nsub(sub(body,2,-2),tag,pre,post) .. post
+ end))
+end
+
+local function totex(str,mode)
+ if not frozen then freeze() end
+ local n = 0
+ -- crap
+ str = gsub(str,"%s+",' ')
+ -- xml
+ str = gsub(str,"&(.-);",entities)
+ -- ...E...
+ str = gsub(str,"([%-%+]?[%d%.%+%-]+)E([%-%+]?[%d%.]+)", "{\\SCINOT{%1}{%2}}")
+ -- ^-..
+ str = gsub(str,"%^([%-%+]*%d+)", "^{%1}")
+ -- ^(...)
+ str = nsub(str,"%^", "^{", "}")
+ -- 1/x^2
+ repeat
+ str, n = gsub(str,"([%d%w%.]+)/([%d%w%.]+%^{[%d%w%.]+})", "\\frac{%1}{%2}")
+ until n == 0
+ -- todo: autoparenthesis
+ -- int(a,b,c)
+ for k, v in next, list_2_1 do
+ repeat str, n = gsub(str,k,v) until n == 0
+ end
+ -- int(a,b)
+ for k, v in next, list_2_2 do
+ repeat str, n = gsub(str,k,v) until n == 0
+ end
+ -- int(a)
+ for k, v in next, list_2_3 do
+ repeat str, n = gsub(str,k,v) until n == 0
+ end
+ -- sin(x) => {\\sin(x)}
+ for k, v in next, list_1_1 do
+ repeat str, n = gsub(str,k,v) until n == 0
+ end
+ -- mean
+ str = nsub(str, "mean", "\\OVERLINE{", "}")
+ -- (1+x)/(1+x) => \\FRAC{1+x}{1+x}
+ repeat
+ str, n = gsub(str,"(%b())/(%b())", function(a,b)
+ return "\\FRAC{" .. sub(a,2,-2) .. "}{" .. sub(b,2,-2) .. "}"
+ end )
+ until n == 0
+ -- (1+x)/x => \\FRAC{1+x}{x}
+ repeat
+ str, n = gsub(str,"(%b())/([%+%-]?[%.%d%w]+)", function(a,b)
+ return "\\FRAC{" .. sub(a,2,-2) .. "}{" .. b .. "}"
+ end )
+ until n == 0
+ -- 1/(1+x) => \\FRAC{1}{1+x}
+ repeat
+ str, n = gsub(str,"([%.%d%w]+)/(%b())", function(a,b)
+ return "\\FRAC{" .. a .. "}{" .. sub(b,2,-2) .. "}"
+ end )
+ until n == 0
+ -- 1/x => \\FRAC{1}{x}
+ repeat
+ str, n = gsub(str,"([%.%d%w]+)/([%+%-]?[%.%d%w]+)", "\\FRAC{%1}{%2}")
+ until n == 0
+ -- times
+ str = gsub(str,"%*", " ")
+ -- symbols -- we can use a table substitution here
+ str = gsub(str,"([<>=][<>=]*)", symbols)
+ -- functions
+ str = nsub(str,"sqrt", "\\SQRT{", "}")
+ str = nsub(str,"exp", "e^{", "}")
+ str = nsub(str,"abs", "\\left|", "\\right|")
+ -- d/D
+ str = nsub(str,"D", "{\\FRAC{\\MBOX{d}}{\\MBOX{d}x}{(", ")}}")
+ str = gsub(str,"D([xy])", "\\FRAC{{\\RM d}%1}{{\\RM d}x}")
+ -- f/g
+ for k,v in next, list_3 do -- todo : prepare k,v
+ str = nsub(str,"D"..v,"{\\RM "..v.."}^{\\PRIME}(",")")
+ str = nsub(str,v,"{\\RM "..v.."}(",")")
+ end
+ -- more symbols
+ for k,v in next, list_4_1 do
+ str = gsub(str,k,v)
+ end
+ -- parenthesis (optional)
+ if mode == 2 then
+ str = gsub(str,"%(", "\\left(")
+ str = gsub(str,"%)", "\\right)")
+ end
+ -- csnames
+ str = gsub(str,"(\\[A-Z]+)", lower)
+ -- report
+ return str
+end
+
+calcmath.totex = totex
+
+function calcmath.tex(str,mode)
+ context(totex(str))
+end
+
+function calcmath.xml(id,mode)
+ context(totex(lxml.id(id).dt[1],mode))
+end
+
+-- work in progress ... lpeg variant
+
+if false then
+
+ -- todo:
+
+ -- maybe rewrite to current lpeg, i.e. string replacement and no Cc's
+
+ -- table approach we have now is less efficient but more flexible
+
+ -- D \frac {\rm d} {{\rm d}x}
+ -- Dx Dy \frac {{\rm d}y} {{\rm d}x}
+ -- Df Dg {\rm f}^{\prime}
+ -- f() g() {\rm f}()
+
+ -- valid utf8
+
+ local S, P, R, C, V, Cc, Ct = lpeg.S, lpeg.P, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc, lpeg.Ct
+
+ local space = S(" \n\r\t")^0
+ local integer = P("-")^-1 * R("09")^1
+ local realpart = P("-")^-1 * R("09")^1 * S(".")^1 * R("09")^1
+ local number = Cc("number") * C(integer) * space
+ local real = Cc("real") * C(realpart) * space
+ local float = Cc("float") * C(realpart) * lpeg.P("E") * lpeg.C(integer) * space
+ local identifier = Cc("identifier") * C(R("az","AZ")) * space
+ local compareop = Cc("compare") * C(P("<") + P("=") + P(">") + P(">=") + P("<=") + P("&gt;") + P("&lt;")) * space
+ local factorop = Cc("factor") * C(S("+-^_,")) * space
+ local termop = Cc("term") * C(S("*/")) * space
+ local constant = Cc("constant") * C(P("pi") + lpeg.P("inf")) * space
+ local functionop = Cc("function") * C(R("az")^1) * space
+ local open = P("(") * space
+ local close = P(")") * space
+
+ local grammar = P {
+ "expression",
+ expression = Ct(V("factor") * ((factorop+compareop) * V("factor"))^0),
+ factor = Ct(V("term") * (termop * V("term"))^0),
+ term = Ct(
+ float + real + number +
+ (open * V("expression") * close) +
+ (functionop * open * (V("expression") * (P(",") * V("expression"))^0) * close) +
+ (functionop * V("term")) +
+ constant + identifier
+ ),
+ }
+
+ local parser = space * grammar * -1
+
+ local function has_factor(t)
+ for i=1,#t do
+ if t[i] == "factor" then
+ return true
+ end
+ end
+ end
+
+ -- can be sped up if needed ...
+
+ function totex(t)
+ if t then
+ local one = t[1]
+ if type(one) == "string" then
+ local two, three = t[2], t[3]
+ if one == "number" then
+ context(two)
+ elseif one == "real" then
+ context(two)
+ elseif one == "float" then
+ context("\\scinot{",two,"}{",three,"}")
+ elseif one == "identifier" then
+ context(two)
+ elseif one == "constant" then
+ context("\\"..two)
+ elseif one == "function" then
+ if two == "sqrt" then
+ context("\\sqrt{")
+ totex(three)
+ context("}")
+ elseif two == "exp" then
+ context(" e^{")
+ totex(three)
+ context("}")
+ elseif two == "abs" then
+ context("\\left|")
+ totex(three)
+ context("\\right|")
+ elseif two == "mean" then
+ context("\\overline{")
+ totex(three)
+ context("}")
+ elseif two == "int" or two == "prod" or two == "sum" then
+ local four, five = t[4], t[5]
+ if five then
+ context("\\"..two.."^{") -- context[two]("{")
+ totex(three)
+ context("}_{")
+ totex(four)
+ context("}")
+ totex(five)
+ elseif four then
+ context("\\"..two.."^{")
+ totex(three)
+ context("}")
+ totex(four)
+ elseif three then
+ context("\\"..two.." ") -- " " not needed
+ totex(three)
+ else
+ context("\\"..two)
+ end
+ else
+ context("\\"..two.."(")
+ totex(three)
+ context(")")
+ end
+ end
+ else
+ local nt = #t
+ local hasfactor = has_factor(t)
+ if hasfactor then
+ context("\\left(")
+ end
+ totex(one)
+ for i=2,nt,3 do
+ local what, how, rest = t[i], t[i+1], t[i+2]
+ if what == "factor" then
+ if how == '^' or how == "_" then
+ context(how)
+ context("{")
+ totex(rest)
+ context("}")
+ else
+ context(how)
+ totex(rest)
+ end
+ elseif what == "term" then
+ if how == '/' then
+ context("\\frac{")
+ totex(rest)
+ context("}{")
+ totex(t[i+3] or "")
+ context("}")
+ elseif how == '*' then
+ context("\\times")
+ totex(rest)
+ else
+ context(how)
+ totex(three)
+ end
+ elseif what == "compare" then
+ if two == ">=" then
+ context("\\ge")
+ elseif two == "<=" then
+ context("\\le")
+ elseif two == "&gt;" then
+ context(">")
+ elseif two == "&lt;" then
+ context("<")
+ end
+ totex(three)
+ end
+ end
+ if hasfactor then
+ context("\\right)")
+ end
+ end
+ end
+ end
+
+ calcmath = { }
+
+ function calcmath.parse(str)
+ return lpegmatch(parser,str)
+ end
+
+ function calcmath.tex(str)
+ str = totex(lpegmatch(parser,str))
+ return (str == "" and "[error]") or str
+ end
+
+end
diff --git a/tex/context/modules/mkiv/x-calcmath.mkiv b/tex/context/modules/mkiv/x-calcmath.mkiv
new file mode 100644
index 000000000..dda88bb3e
--- /dev/null
+++ b/tex/context/modules/mkiv/x-calcmath.mkiv
@@ -0,0 +1,80 @@
+%D \module
+%D [ file=m-calcmath,
+%D version=2006.04.24, % 1999.11.06,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Calculator Math,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Lua code.
+
+\registerctxluafile{x-calcmath}{}
+
+\def\ctxmodulecalcmath#1{\ctxlua{moduledata.calcmath.#1}}
+
+%D Interface:
+
+\unprotect
+
+\unexpanded\def\inlinecalcmath #1{\mathematics{\ctxmodulecalcmath{tex("#1",1)}}}
+\unexpanded\def\displaycalcmath#1{\startformula\ctxmodulecalcmath{tex("#1",2)}\stopformula}
+
+\let\calcmath\inlinecalcmath
+
+\let\icm\inlinecalcmath
+\let\dcm\displaycalcmath
+
+\startxmlsetups xml:cam:define
+ \xmlsetsetup {\xmldocument} {cam:*} {*}
+ \xmlsetsetup {\xmldocument} {(icm|dcm)} {cam:*}
+\stopxmlsetups
+
+\xmlregisterns{cam}{calcmath}
+
+\xmlregistersetup{xml:cam:define}
+
+% tex -> lua -> tex -> lua -> tex
+% \mathematics{\ctxmodulecalcmath{xml(\!!bs\xmlflush{#1}\!!es,1)}}
+% tex -> lua -> tex
+% \mathematics{\ctxmodulecalcmath{xml("#1",1)}}%
+
+\startxmlsetups cam:i
+ \mathematics{\ctxmodulecalcmath{xml("#1",1)}}%
+\stopxmlsetups
+
+\startxmlsetups cam:d
+ \startformula\ctxmodulecalcmath{xml("#1",2)}\stopformula
+\stopxmlsetups
+
+\startxmlsetups cam:icm
+ \mathematics{\ctxmodulecalcmath{xml("#1",1)}}
+\stopxmlsetups
+
+\startxmlsetups cam:dcm
+ \startformula\ctxmodulecalcmath{xml("#1",2)}\stopformula
+\stopxmlsetups
+
+\protect \endinput
+
+\starttext
+
+% \calcmath{sin(x) + x^2 + x^(1+x) + 1/x^2 + mean(x+mean(y))}
+% \calcmath{int(a,b,c)}
+% \calcmath{(1+x)/(1+x) + (1+x)/(1+(1+x)/(1+x))}
+% \calcmath{10E-2}
+% \calcmath{(1+x)/x}
+% \calcmath{(1+x)/12}
+% \calcmath{(1+x)/-12}
+% \calcmath{1/-12}
+% \calcmath{12x/(1+x)}
+% \calcmath{exp(x+exp(x+1))}
+% \calcmath{abs(x+abs(x+1)) + pi + inf}
+% \calcmath{Dx Dy}
+% \calcmath{D(x+D(y))}
+% \calcmath{Df(x)}
+% \calcmath{g(x)}
diff --git a/tex/context/modules/mkiv/x-cals.lua b/tex/context/modules/mkiv/x-cals.lua
new file mode 100644
index 000000000..3af6106d8
--- /dev/null
+++ b/tex/context/modules/mkiv/x-cals.lua
@@ -0,0 +1,221 @@
+if not modules then modules = { } end modules ['x-cals'] = {
+ version = 1.001,
+ comment = "companion to x-cals.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local next = next
+local format, lower = string.format, string.lower
+local xmlsprint, xmlcprint, xmlcollected, xmlelements = xml.sprint, xml.cprint, xml.collected, xml.elements
+local n_todimen, s_todimen = number.todimen, string.todimen
+
+-- there is room for speedups as well as cleanup (using context functions)
+
+local cals = { }
+moduledata.cals = cals
+lxml.mathml = cals -- for the moment
+
+cals.ignore_widths = false
+cals.shrink_widths = false
+cals.stretch_widths = false
+
+-- the following flags only apply to columns that have a specified width
+--
+-- proportional : shrink or stretch proportionally to the width
+-- equal : shrink or stretch equaly distributed
+-- n < 1 : shrink or stretch proportionally to the width but multiplied by n
+--
+-- more clever things, e.g. the same but applied to unspecified widths
+-- has to happen at the core-ntb level (todo)
+
+local halignments = {
+ left = "flushleft",
+ right = "flushright",
+ center = "middle",
+ centre = "middle",
+ justify = "normal",
+}
+
+local valignments = {
+ top = "high",
+ bottom = "low",
+ middle = "lohi",
+}
+
+local function adapt(widths,b,w,delta,sum,n,what)
+ if b == "equal" then
+ delta = delta/n
+ for k, v in next, w do
+ widths[k] = n_todimen(v - delta)
+ end
+ elseif b == "proportional" then
+ delta = delta/sum
+ for k, v in next, w do
+ widths[k] = n_todimen(v - v*delta)
+ end
+ elseif type(b) == "number" and b < 1 then
+ delta = b*delta/sum
+ for k, v in next, w do
+ widths[k] = n_todimen(v - v*delta)
+ end
+ end
+end
+
+local function getspecs(root, pattern, names, widths)
+ -- here, but actually we need this in core-ntb.tex
+ -- but ideally we need an mkiv enhanced core-ntb.tex
+ local ignore_widths = cals.ignore_widths
+-- local shrink_widths = at.option == "shrink" or cals.shrink_widths
+-- local stretch_widths = at.option == "stretch" or cals.stretch_widths
+ local shrink_widths = cals.shrink_widths
+ local stretch_widths = cals.stretch_widths
+ for e in xmlcollected(root,pattern) do
+ local at = e.at
+ local column = at.colnum
+ if column then
+ if not ignore_widths then
+ local width = at.colwidth
+ if width then
+ widths[tonumber(column)] = lower(width)
+ end
+ end
+ local name = at.colname
+ if name then
+ names[name] = tonumber(column)
+ end
+ end
+ end
+ if ignore_width then
+ -- forget about it
+ elseif shrink_widths or stretch_widths then
+ local sum, n, w = 0, 0, { }
+ for _, v in next, widths do
+ n = n + 1
+ v = (type(v) == "string" and s_todimen(v)) or v
+ if v then
+ w[n] = v
+ sum = sum + v
+ end
+ end
+ local hsize = tex.hsize
+ if type(hsize) == "string" then
+ hsize = s_todimen(hsize)
+ end
+ local delta = sum - hsize
+ if shrink_widths and delta > 0 then
+ adapt(widths,shrink_widths,w,delta,sum,n,"shrink")
+ elseif stretch_widths and delta < 0 then
+ adapt(widths,stretch_widths,w,delta,sum,n,"stretch")
+ end
+ end
+end
+
+local function getspans(root, pattern, names, spans)
+ for e in xmlcollected(root,pattern) do
+ local at = e.at
+ local name, namest, nameend = at.colname, names[at.namest or "?"], names[at.nameend or "?"]
+ if name and namest and nameend then
+ spans[name] = tonumber(nameend) - tonumber(namest) + 1
+ end
+ end
+end
+
+local bTR, eTR, bTD, eTD = context.bTR, context.eTR, context.bTD, context.eTD
+
+function cals.table(root,namespace)
+
+ local prefix = (namespace or "cals") .. ":"
+
+ local prefix = namespace and namespace ~= "" and (namespace .. ":") or ""
+ local p = "/" .. prefix
+
+ local tgroupspec = p .. "tgroup"
+ local colspec = p .. "colspec"
+ local spanspec = p .. "spanspec"
+ local hcolspec = p .. "thead" .. p .. "colspec"
+ local bcolspec = p .. "tbody" .. p .. "colspec"
+ local fcolspec = p .. "tfoot" .. p .. "colspec"
+ local entryspec = p .. "entry" .. "|" .. prefix .. "entrytbl" -- shouldn't that be p ?
+ local hrowspec = p .. "thead" .. p .. "row"
+ local browspec = p .. "tbody" .. p .. "row"
+ local frowspec = p .. "tfoot" .. p .. "row"
+
+ local function tablepart(root, xcolspec, xrowspec, before, after) -- move this one outside
+ before()
+ local at = root.at
+ local pphalign, ppvalign = at.align, at.valign
+ local names, widths, spans = { }, { }, { }
+ getspecs(root, colspec , names, widths)
+ getspecs(root, xcolspec, names, widths)
+ getspans(root, spanspec, names, spans)
+ for r, d, k in xmlelements(root,xrowspec) do
+ bTR()
+ local dk = d[k]
+ local at = dk.at
+ local phalign, pvalign = at.align or pphalign, at.valign or ppvalign -- todo: __p__ test
+ local col = 1
+ for rr, dd, kk in xmlelements(dk,entryspec) do
+ local dk = dd[kk]
+ if dk.tg == "entrytbl" then
+ -- bTD(function() cals.table(dk) end)
+ bTD()
+ context("{")
+ cals.table(dk)
+ context("}")
+ eTD()
+ col = col + 1
+ else
+ local at = dk.at
+ local b, e, s, m = names[at.namest or "?"], names[at.nameend or "?"], spans[at.spanname or "?"], at.morerows
+ local halign, valign = at.align or phalign, at.valign or pvalign
+ if b and e then
+ s = e - b + 1
+ end
+ if halign then
+ halign = halignments[halign]
+ end
+ if valign then
+ valign = valignments[valign]
+ end
+ local width = widths[col]
+ if s or m or halign or valign or width then -- currently only english interface !
+ bTD {
+ nx = s or 1,
+ ny = (m or 0) + 1,
+ align = format("{%s,%s}",halign or "flushleft",valign or "high"),
+ width = width or "fit",
+ }
+ else
+ bTD {
+ align = "{flushleft,high}",
+ width = "fit", -- else problems with vertical material
+ }
+ end
+ xmlcprint(dk)
+ eTD()
+ col = col + (s or 1)
+ end
+ end
+ eTR()
+ end
+ after()
+ end
+
+ for tgroup in lxml.collected(root,tgroupspec) do
+ context.directsetup("cals:table:before")
+ lxml.directives.before(root,"cdx") -- "cals:table"
+ context.bgroup()
+ lxml.directives.setup(root,"cdx") -- "cals:table"
+ context.bTABLE()
+ tablepart(tgroup, hcolspec, hrowspec, context.bTABLEhead, context.eTABLEhead)
+ tablepart(tgroup, bcolspec, browspec, context.bTABLEbody, context.eTABLEbody)
+ tablepart(tgroup, fcolspec, frowspec, context.bTABLEfoot, context.eTABLEfoot)
+ context.eTABLE()
+ context.egroup()
+ lxml.directives.after(root,"cdx") -- "cals:table"
+ context.directsetup("cals:table:after")
+ end
+
+end
diff --git a/tex/context/modules/mkiv/x-cals.mkiv b/tex/context/modules/mkiv/x-cals.mkiv
new file mode 100644
index 000000000..31ba2d213
--- /dev/null
+++ b/tex/context/modules/mkiv/x-cals.mkiv
@@ -0,0 +1,45 @@
+%D \module
+%D [ file=x-cals,
+%D version=2007.09.05,
+%D title=\CONTEXT\ XML Modules,
+%D subtitle=Cals table renderer,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt XML Macros / Cals Tables}
+
+\startmodule [cals]
+
+\registerctxluafile{x-cals}{}
+
+% \startxmlsetups xml:cals:process
+% \xmlsetsetup {#1} {cals:table} {*}
+% \stopxmlsetups
+% \startxmlsetups cals:table
+% \ctxlua{moduledata.cals.table("#1")}
+% \stopxmlsetups
+% \xmlregistersetup{xml:cals:process}
+
+\startxmlsetups xml:cals:process
+% \xmlsetfunction {\xmldocument} {cals:table} {moduledata.cals.table}
+ \xmlsetfunction {#1} {cals:table} {moduledata.cals.table}
+\stopxmlsetups
+
+\startxmlsetups xml:cals:nonamespace
+% \xmlsetfunction {\xmldocument} {table} {moduledata.cals.table}
+ \xmlsetfunction {#1} {table} {moduledata.cals.table}
+\stopxmlsetups
+
+\xmlregistersetup{xml:cals:process}
+
+\xmlregisterns{cals}{cals}
+
+%D One can register the nonamespace variant for cases where the cals
+%D model is embedded in the parent DTD.
+
+\stopmodule
diff --git a/tex/context/modules/mkiv/x-chemml.lua b/tex/context/modules/mkiv/x-chemml.lua
new file mode 100644
index 000000000..79c1d9525
--- /dev/null
+++ b/tex/context/modules/mkiv/x-chemml.lua
@@ -0,0 +1,51 @@
+if not modules then modules = { } end modules ['x-chemml'] = {
+ version = 1.001,
+ comment = "companion to x-chemml.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- not yet acceptable cld
+
+local format, lower, upper, gsub, sub, match = string.format, string.lower, string.upper, string.gsub, string.sub, string.match
+local concat = table.concat
+
+local chemml = { }
+local moduledata = moduledata or { }
+moduledata.chemml = chemml
+
+function chemml.pi(id)
+ local str = xml.content(lxml.id(id))
+ local _, class, key, value = match(str,"^(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s*$")
+ if key and value then
+ context("\\setupCMLappearance[%s][%s=%s]",class, key, value)
+ end
+end
+
+function chemml.do_graphic(id)
+ local t = { }
+ for r, d, k in xml.elements(lxml.id(id),"cml:graphic") do
+ t[#t+1] = xml.tostring(d[k].dt)
+ end
+ context(concat(t,","))
+end
+
+function chemml.no_graphic(id)
+ local t = { }
+ for r, d, k in xml.elements(lxml.id(id),"cml:text|cml:oxidation|cml:annotation") do
+ local dk = d[k]
+ if dk.tg == "oxidation" then
+ t[#t+1] = format("\\chemicaloxidation{%s}{%s}{%s}",r.at.sign or "",r.at.n or 1,xml.tostring(dk.dt))
+ elseif dk.tg == "annotation" then
+ local location = r.at.location or "r"
+ local caption = xml.content(xml.first(dk,"cml:caption"))
+ local text = xml.content(xml.first(dk,"cml:text"))
+ t[#t+1] = format("\\doCMLannotation{%s}{%s}{%s}",location,caption,text)
+ else
+ t[#t+1] = xml.tostring(dk.dt) or ""
+ end
+ end
+ context(concat(t,","))
+end
+
diff --git a/tex/context/modules/mkiv/x-chemml.mkiv b/tex/context/modules/mkiv/x-chemml.mkiv
new file mode 100644
index 000000000..bb9065921
--- /dev/null
+++ b/tex/context/modules/mkiv/x-chemml.mkiv
@@ -0,0 +1,228 @@
+%D \module
+%D [ file=x-cml,
+%D version=2007.09.03, % reimplementation
+%D title=\CONTEXT\ XML Modules,
+%D subtitle=MkIV ChemML renderer,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% This needs an update!
+
+\writestatus{loading}{ConTeXt XML Macros / Chemistry}
+
+\registerctxluafile{x-chemml}{}
+
+\def\ctxmodulechemml#1{\ctxlua{moduledata.chemml.#1}}
+
+%D The following code assumes a load||flush approach to \XML.
+
+\unprotect
+
+\startxmlsetups xml:cml:process
+ \xmlstrip {#1} {cml:chem|cml:ichem|cml:dchem|cml:reaction|cml:molecule|cml:ion|cml:structure}
+
+ \xmlgrab {#1} {cml:*} {cml:*}
+ \xmlgrab {#1} {cml:gives|cml:equilibrium|cml:mesomeric} {cml:arrow}
+ \xmlgrab {#1} {cml:plus|cml:minus|cml:equal} {cml:operator}
+ \xmlgrab {#1} {cml:bond|cml:singlebond|cml:doublebound|cml:triplebond} {cml:bond}
+
+ \xmlgrab {#1} {pi::chemml} {cml:pi}
+\stopxmlsetups
+
+\xmlregistersetup{xml:cml:process}
+
+\xmlregisterns{cml}{chemml}
+
+\unexpanded\def\setupCMLappearance[#1]{\dodoubleargument\getparameters[@@CML#1]} % old stuff
+
+\setupCMLappearance [ion] [\c!alternative=\v!a]
+
+\def\doifelseCMLvariable#1#2#3% id key value
+ {\doifelse{\xmlatt{#1}{#2}}{#3}
+ \firstoftwoarguments
+ {\doifelse{\getvalue{@@CML\xmltag{#1}#2}}{#3}
+ \firstoftwoarguments
+ \secondoftwoarguments}}
+
+\startxmlsetups cml:pi
+ \ctxmodulechemml{pi(#1)}
+\stopxmlsetups
+
+\startxmlsetups cml:chem
+ \automathematics{\xmlflush{#1}}
+\stopxmlsetups
+\startxmlsetups cml:ichem
+ \inlinemathematics{\xmlflush{#1}}
+\stopxmlsetups
+\startxmlsetups cml:dchem
+ \displaymathematics{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups cml:reaction
+ \xmlflush{#1}
+\stopxmlsetups
+
+\unexpanded\def\doCMLtext#1#2#3% main top bot
+ {\setbox0\hbox{\doifsomething{#2}{\txx\setstrut\strut\ignorespaces#2\unskip}}%
+ \setbox2\hbox{\ignorespaces\strut#1\unskip}%
+ \setbox4\hbox{\doifsomething{#3}{\txx\setstrut\strut\ignorespaces#3\unskip}}%
+ \scratchdimen=\wd2\advance\scratchdimen-.5em
+ \ifdim\wd0>\scratchdimen
+ \setbox0\hbox spread .5em{\hss\box0\hss}%
+ \fi
+ \ifdim\wd4>\scratchdimen
+ \setbox4\hbox spread .5em{\hss\box4\hss}%
+ \fi
+ \setbox6=\vbox
+ {\offinterlineskip\halign{\hss##\hss\cr\copy0\cr\copy2\cr\copy4\cr}}%
+ \hbox{\lower\ht4\hbox{\lower\dp2\box6}}}
+
+\def\doCMLamount#1%
+ {\scratchcounter0\xmlatt{#1}{n}\relax
+ \ifnum\scratchcounter>0 \number\scratchcounter \fi}
+
+\startxmlsetups cml:molecule
+ \doCMLtext
+ {\doCMLamount{#1}
+ \xmlall{#1}{cml:atom|cml:bond|cml:singlebond|cml:doublebond|cml:triplebond}}
+ {\xmlindex{#1}{cml:caption}{2}}
+ {\xmlindex{#1}{cml:caption}{1}}
+\stopxmlsetups
+
+\startxmlsetups cml:atom
+ \doCMLtext {
+ \lohi {
+ $\tfxx\xmlatt{#1}{protons}$
+ } {
+ $\tfxx\xmlatt{#1}{weight}$
+ }
+ \xmlflush{#1}
+ \lohi {
+ $\tfxx\xmlatt{#1}{n}$
+ } {
+ $\tfxx\xmlatt{#1}{charge}$%
+ }
+ }
+ {\xmlindex{#1}{cml:caption}{2}}
+ {\xmlindex{#1}{cml:caption}{1}}
+\stopxmlsetups
+
+\startxmlsetups cml:ion
+ \doifelseCMLvariable{#1}{alternative}{b} {
+ \left[
+ \doCMLtext
+ {\doCMLamount{#1}
+ \xmlall{#1}{cml:atom}}
+ {\xmlindex{#1}{cml:caption}{2}}
+ {\xmlindex{#1}{cml:caption}{1}}
+ \right]
+ } {
+ \doCMLtext
+ {\doCMLamount{#1}
+ \xmlall{#1}{cml:atom}}
+ {\xmlindex{#1}{cml:caption}{2}}
+ {\xmlindex{#1}{cml:caption}{1}}
+ }
+ \high {\xmlatt{#1}{charge}}
+\stopxmlsetups
+
+\def\doCMLgives {\xrightarrow}
+\def\doCMLequilibrium{\xleftrightarrow}
+\def\doCMLmesomeric {\xrightoverleftarrow}
+\def\doCMLplus {+}
+\def\doCMLminus {-}
+\def\doCMLequal {=}
+
+\startxmlsetups cml:arrow
+ \quad
+ \executeifdefined{doCML\xmlname{#1}}\doCMLgives{\tf\xmlindex{#1}{cml:caption}{2}}{\tf\xmlindex{#1}{cml:caption}{1}}
+ \quad
+\stopxmlsetups
+
+\startxmlsetups cml:operator
+ \quad
+ \mathop{\executeifdefined{doCML\xmlname{#1}}\doCMLplus}
+ \quad
+\stopxmlsetups
+
+\startxmlsetups cml:bond
+ \executeifdefined{doCML\xmlname{#1}} {
+ \ifcase0\xmlatt{#1}{n}\relax
+ \doCMLsinglebond
+ \or
+ \doCMLdoublebond
+ \or
+ \doCMLtriplebond
+ \fi
+ }
+\stopxmlsetups
+
+\def\doCMLbond
+ {\hrule\s!width\hsize\s!height.1ex\relax}
+
+\def\dodoCMLbond#1#2#3%
+ {\begingroup
+ \setbox\scratchbox\hbox{$M$}%
+ \vbox to \ht\scratchbox
+ {\hsize\wd\scratchbox
+ \vskip.1\wd\scratchbox
+ #1\vfill#2\vfill#3%
+ \vskip.1\wd\scratchbox}%
+ \endgroup}
+
+\def\doCMLsinglebond{\dodoCMLbond\relax \doCMLbond\relax }
+\def\doCMLdoublebond{\dodoCMLbond\doCMLbond\relax \doCMLbond}
+\def\doCMLtriplebond{\dodoCMLbond\doCMLbond\doCMLbond\doCMLbond}
+
+\startxmlsetups cml:structure
+ \startchemical
+ \xmlall{#1}{cml:component}
+ \stopchemical
+\stopxmlsetups
+
+% It makes not much sense to adapt ppchtex to accept different input. Maybe some day.
+
+\startxmlsetups cml:component
+ \expanded {
+ \chemical
+ [\ctxmodulechemml{do_graphic("#1")}]
+ [\ctxmodulechemml{no_graphic("#1")}]
+ }
+\stopxmlsetups
+
+\unexpanded\def\doCMLannotation#1% #2#3% loc caption text
+ {\xmlval{cml:a:l}{#1}{\chemicalright}}% {#2}{#3}}
+
+\xmlmapvalue {cml:a:l} {t} {\chemicaltop}
+\xmlmapvalue {cml:a:l} {b} {\chemicalbottom}
+\xmlmapvalue {cml:a:l} {l} {\chemicalleft}
+\xmlmapvalue {cml:a:l} {r} {\chemicalright}
+\xmlmapvalue {cml:a:l} {lc} {\chemicalleftcentered} % \xmlmapvalue {cml:a:l} {cl} {\chemicalleftcentered}
+\xmlmapvalue {cml:a:l} {rc} {\chemicalrightcentered} % \xmlmapvalue {cml:a:l} {cr} {\chemicalrightcentered}
+\xmlmapvalue {cml:a:l} {tl} {\chemicaltopleft} % \xmlmapvalue {cml:a:l} {lt} {\chemicaltopleft}
+\xmlmapvalue {cml:a:l} {bl} {\chemicalbottomleft} % \xmlmapvalue {cml:a:l} {lb} {\chemicalbottomleft}
+\xmlmapvalue {cml:a:l} {tr} {\chemicaltopright} % \xmlmapvalue {cml:a:l} {rt} {\chemicaltopright}
+\xmlmapvalue {cml:a:l} {br} {\chemicalbottomright} % \xmlmapvalue {cml:a:l} {rb} {\chemicalbottomright}
+\xmlmapvalue {cml:a:l} {lt} {\chemicallefttop} % \xmlmapvalue {cml:a:l} {tl} {\chemicallefttop}
+\xmlmapvalue {cml:a:l} {lb} {\chemicalleftbottom} % \xmlmapvalue {cml:a:l} {bl} {\chemicalleftbottom}
+\xmlmapvalue {cml:a:l} {rt} {\chemicalrighttop} % \xmlmapvalue {cml:a:l} {tr} {\chemicalrighttop}
+\xmlmapvalue {cml:a:l} {rb} {\chemicalrightbottom} % \xmlmapvalue {cml:a:l} {br} {\chemicalrightbottom}
+\xmlmapvalue {cml:a:l} {x} {\chemicaltighttext}
+\xmlmapvalue {cml:a:l} {sl} {\chemicalsmashedleft} % \xmlmapvalue {cml:a:l} {ls} {\chemicalsmashedleft}
+\xmlmapvalue {cml:a:l} {sm} {\chemicalsmashedmiddle} % \xmlmapvalue {cml:a:l} {ms} {\chemicalsmashedmiddle}
+\xmlmapvalue {cml:a:l} {sr} {\chemicalsmashedright} % \xmlmapvalue {cml:a:l} {rs} {\chemicalsmashedright}
+
+\startxmlsetups cml:forever
+ \left[\xmlflush{#1}\right]
+\stopxmlsetups
+
+% \starttext
+% \xmlprocess{main}{cmltest.xml}{xml:process}
+% \stoptext
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/x-ct.lua b/tex/context/modules/mkiv/x-ct.lua
new file mode 100644
index 000000000..9c647e8e7
--- /dev/null
+++ b/tex/context/modules/mkiv/x-ct.lua
@@ -0,0 +1,167 @@
+if not modules then modules = { } end modules ['x-ct'] = {
+ version = 1.001,
+ comment = "companion to x-ct.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- needs testing
+
+local xmlsprint, xmlcprint, xmlfilter, xmlcollected = xml.sprint, xml.cprint, xml.filter, xml.collected
+local format, concat, rep, find = string.format, table.concat, string.rep, string.find
+
+moduledata.ct = moduledata.ct or { }
+
+local halignments = {
+ left = 'l',
+ flushleft = 'l',
+ right = 'r',
+ flushright = 'r',
+ center = 'c',
+ middle = 'c',
+ centre = 'c',
+ justify = '',
+}
+
+local templates = { }
+
+function moduledata.ct.registertabulatetemplate(name,str)
+ templates[name] = str
+end
+
+local function roottemplate(root)
+ local rt = root.at.template
+ if rt then
+ local template = templates[rt]
+ if template then
+ return template
+ else
+ if not find(rt,"|") then
+ rt = gsub(rt,",","|")
+ end
+ if not find(rt,"^|") then rt = "|" .. rt end
+ if not find(rt,"|$") then rt = rt .. "|" end
+ return rt
+ end
+ end
+end
+
+local function specifiedtemplate(root,templatespec)
+ local template = { }
+ for e in xmlcollected(root,templatespec) do
+ local at = e.at
+ local tm = halignments[at.align] or ""
+ if toboolean(at.paragraph) then
+ tm = tm .. "p"
+ end
+ template[#template+1] = tm
+ end
+ if #template > 0 then
+ return "|" .. concat(template,"|") .. "|"
+ else
+ return nil
+ end
+end
+
+local function autotemplate(root,rowspec,cellspec)
+ local max = 0
+ for e in xmlcollected(root,rowspec) do
+ local n = xml.count(e,cellspec)
+ if n > max then max = n end
+ end
+ if max == 2 then
+ return "|l|p|"
+ elseif max > 0 then
+ return "|" .. rep("p|",max)
+ else
+ return nil
+ end
+end
+
+local defaulttemplate = "|l|p|"
+
+function moduledata.ct.tabulate(root,namespace)
+ if not root then
+ return
+ else
+ root = lxml.id(root)
+ end
+
+ local prefix = (namespace or "context") .. ":"
+
+ local templatespec = "/" .. prefix .. "template" .. "/" .. prefix .. "column"
+ local bodyrowspec = "/" .. prefix .. "body" .. "/" .. prefix .. "row"
+ local cellspec = "/" .. prefix .. "cell"
+
+ local template =
+ roottemplate (root) or
+ specifiedtemplate (root,templatespec) or
+ autotemplate (root,bodyrowspec,cellspec) or
+ defaulttemplate
+
+ -- todo: head and foot
+
+ local NC, NR = context.NC, context.NR
+
+ lxml.directives.before(root,'cdx')
+ context.bgroup()
+ lxml.directives.setup(root,'cdx')
+ context.starttabulate { template }
+ for e in xmlcollected(root,bodyrowspec) do
+ NC()
+ for e in xmlcollected(e,cellspec) do
+ xmlcprint(e)
+ NC()
+ end
+ NR()
+ end
+ context.stoptabulate()
+ context.egroup()
+ lxml.directives.after(root,'cdx')
+
+end
+
+-- todo: use content and caption
+
+function moduledata.ct.combination(root,namespace)
+
+ if not root then
+ return
+ else
+ root = lxml.id(root)
+ end
+
+ local prefix = (namespace or "context") .. ":"
+
+ local pairspec = "/" .. prefix .. "pair"
+ local contentspec = "/" .. prefix .. "content" .. "/text()"
+ local captionspec = "/" .. prefix .. "caption" .. "/text()"
+
+ local nx, ny = root.at.nx, root.at.ny
+
+ if not (nx or ny) then
+ nx = xml.count(root,pairspec) or 2
+ end
+ local template = format("%s*%s", nx or 1, ny or 1)
+
+ lxml.directives.before(root,'cdx')
+ context.bgroup()
+ lxml.directives.setup(root,'cdx')
+ context.startcombination { template }
+ for e in xmlcollected(root,pairspec) do
+ -- context.combination(
+ -- function() xmlfilter(e,contentspec) end,
+ -- function() xmlfilter(e,captionspec) end
+ -- )
+ context("{")
+ xmlfilter(e,contentspec)
+ context("}{")
+ xmlfilter(e,captionspec)
+ context("}")
+ end
+ context.stopcombination()
+ context.egroup()
+ lxml.directives.after(root,'cdx')
+
+end
diff --git a/tex/context/modules/mkiv/x-ct.mkiv b/tex/context/modules/mkiv/x-ct.mkiv
new file mode 100644
index 000000000..6a6352f03
--- /dev/null
+++ b/tex/context/modules/mkiv/x-ct.mkiv
@@ -0,0 +1,29 @@
+%D \module
+%D [ file=x-cals,
+%D version=2007.09.05,
+%D title=\CONTEXT\ XML Modules,
+%D subtitle=\CONTEXT\ Structures,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt XML Macros / Basics}
+
+\startmodule [ct]
+
+\registerctxluafile{x-ct}{}
+
+\startxmlsetups xml:context:process
+ \xmlsetfunction {\xmldocument} {context:tabulate} {moduledata.ct.tabulate}
+ \xmlsetfunction {\xmldocument} {context:combination} {moduledata.ct.combination}
+\stopxmlsetups
+
+\xmlregistersetup{xml:context:process}
+
+\xmlregisterns{context}{context}
+
+\stopmodule
diff --git a/tex/context/modules/mkiv/x-entities.mkiv b/tex/context/modules/mkiv/x-entities.mkiv
new file mode 100644
index 000000000..3dd02118a
--- /dev/null
+++ b/tex/context/modules/mkiv/x-entities.mkiv
@@ -0,0 +1,18 @@
+%D \module
+%D [ file=x-entities,
+%D version=2008.05.29,
+%D title=\CONTEXT\ XML Modules,
+%D subtitle=\HTML\ entities,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt XML Macros / HTML entities}
+
+\ctxlua{characters.registerentities()}
+
+\endinput
diff --git a/tex/context/modules/mkiv/x-foxet.mkiv b/tex/context/modules/mkiv/x-foxet.mkiv
new file mode 100644
index 000000000..80fe7e500
--- /dev/null
+++ b/tex/context/modules/mkiv/x-foxet.mkiv
@@ -0,0 +1,29 @@
+%D \module
+%D [ file=x-foxet,
+%D version=2004.03.12, % based on earlier experiments
+%D title=\FOXET,
+%D subtitle=Formatting Objects,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Nota bene: I will reimplement formatting object in proper
+%D \MKIV\ code.
+
+\writestatus{foxet}{the mkiv variant is under construction}
+
+%D This module is just a shortcut for:
+
+% fo = basic formatting objects
+% fe = basic formatting extensions
+% fx = extra formatting objects
+% fu = user formatting objects
+% fs = setup
+
+% \usemodule[fo,fe,fx,fu,fs,mathml]
+
+\endinput
diff --git a/tex/context/modules/mkiv/x-html.mkiv b/tex/context/modules/mkiv/x-html.mkiv
new file mode 100644
index 000000000..e1806eb9e
--- /dev/null
+++ b/tex/context/modules/mkiv/x-html.mkiv
@@ -0,0 +1,379 @@
+%D \module
+%D [ file=x-html,
+%D version=2011.02.03, % adapted 2014.11.08
+%D title=\CONTEXT\ Modules,
+%D subtitle=HTML,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\startmodule[html]
+
+%D Usage:
+%D
+%D \starttyping
+%D \xmlregistersetup{xml:html:basics}
+%D \xmlregistersetup{xml:html:tables}
+%D \stoptyping
+
+% \xmlsetsetup{#1}{(p|span)[@lang]}{xml:html:lang}
+%
+% \startxmlsetups xml:html:lang
+% \begingroup
+% \language[\xmlatt{#1}{lang}]
+% \xmlsetup{#1}{xml:html:\xmltag{#1}}
+% \endgroup
+% \stopxmlsetups
+
+\unprotect
+
+\definehighlight[b] [\c!command=\v!no,\c!style=\v!bold]
+\definehighlight[i] [\c!command=\v!no,\c!style=\v!italic]
+\definehighlight[bi] [\c!command=\v!no,\c!style=\v!bolditalic]
+\definehighlight[em] [\c!command=\v!no,\c!style=\em]
+\definehighlight[tt] [\c!command=\v!no,\c!style=\v!mono]
+\definehighlight[strong][\c!command=\v!no,\c!style=\v!bold]
+\definehighlight[u] [\c!command=\v!no,\c!style=\directsetbar{\v!underbar}]
+\definehighlight[code] [\c!command=\v!no,\c!style=\v!mono]
+\definehighlight[pre] [\c!command=\v!no]
+
+\protect
+
+% todo: pre
+
+\startxmlsetups xml:html:basics
+ \xmlsetsetup{#1}{p|br|b|i|u|em|tt|strong|ul|ol|li|table|thead|tbody|tfoot|tr|td|th|span|img}{xml:html:*}
+ \xmlsetsetup{#1}{b/i}{xml:html:bi}
+ \xmlsetsetup{#1}{i/b}{xml:html:bi}
+ \xmlstripanywhere{#1}{!pre}
+\stopxmlsetups
+
+\startxmlsetups xml:html:tables
+ \xmlsetsetup{#1}{table|thead|tbody|tfoot|tr|td|th}{xml:html:*}
+\stopxmlsetups
+
+\startxmlsetups xml:html:p
+ \xmldoifnotselfempty {#1} {
+ \dontleavehmode
+ \ignorespaces
+ \xmlflush{#1}
+ \removeunwantedspaces
+ }
+ \par
+\stopxmlsetups
+
+\startxmlsetups xml:html:br
+ \crlf
+\stopxmlsetups
+
+\startxmlsetups xml:html:b
+ \directhighlight{b}{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups xml:html:i
+ \directhighlight{i}{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups xml:html:bi
+ \directhighlight{bi}{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups xml:html:em
+ \directhighlight{em}{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups xml:html:tt
+ \directhighlight{tt}{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups xml:html:strong
+ \directhighlight{strong}{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups xml:html:u
+ \directhighlight{u}{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups xml:html:ul
+ \startitemize[packed]
+ \xmlflush{#1}
+ \stopitemize
+\stopxmlsetups
+
+\startxmlsetups xml:html:ol
+ \startitemize[packed,n]
+ \xmlflush{#1}
+ \stopitemize
+\stopxmlsetups
+
+\startxmlsetups xml:html:li
+ \startitem
+ \xmlflush{#1}
+ \stopitem
+\stopxmlsetups
+
+\startxmlsetups xml:html:code
+ \directhighlight{code}{\xmlflushspacewise{#1}}
+\stopxmlsetups
+
+\startxmlsetups xml:html:pre
+ \directhighlight{pre}{\xmlflushspacewise{#1}}
+\stopxmlsetups
+
+\startxmlsetups xml:html:span
+ \xmlflush{#1}
+\stopxmlsetups
+
+\startxmlsetups xml:html:img
+ \ifhmode
+ \dontleavehmode
+ \externalfigure[\xmlatt{#1}{src}]
+ \else
+ \startlinecorrection
+ \externalfigure[\xmlatt{#1}{src}]
+ \stoplinecorrection
+ \fi
+\stopxmlsetups
+
+% tables, maybe we need a generic html table module
+%
+% todo: align
+
+% beware, the padding code is somewhat experimental, eventually the
+% table will be done in cld code
+%
+% we can also use \xmlmap for border etc
+
+\starttexdefinition cssgetsinglepadding #1
+ \ctxlua {
+ context((moduledata.css.padding(
+ "#1",
+ \number\dimexpr0.1ex,
+ \number\dimexpr0.01\hsize,
+ \number\dimexpr1ex,
+ \number\dimexpr1em
+ ))) % returns 4 values therefore ()
+ }sp
+\stoptexdefinition
+
+\startxmlsetups xml:html:table
+ \edef\CellPadding{\xmlatt{#1}{cellpadding}}
+ \ifx\CellPadding\empty
+ \edef\CellPadding{.25ex}
+ \else
+ \edef\CellPadding{\cssgetsinglepadding\CellPadding}
+ \fi
+ \startlinecorrection[blank]
+ \doifelse {\xmlatt{#1}{border}} {0} {
+ \bTABLE[frame=off,offset=\CellPadding]
+ \xmlflush{#1}
+ \eTABLE
+ } {
+ \bTABLE[offset=\CellPadding]
+ \xmlflush{#1}
+ \eTABLE
+ }
+ \stoplinecorrection
+\stopxmlsetups
+
+\startxmlsetups xml:html:thead
+ \bTABLEhead
+ \xmlflush{#1}
+ \eTABLEhead
+\stopxmlsetups
+
+\startxmlsetups xml:html:tbody
+ \bTABLEbody
+ \xmlflush{#1}
+ \eTABLEbody
+\stopxmlsetups
+
+\startxmlsetups xml:html:tfoot
+ \bTABLEfoot
+ \xmlflush{#1}
+ \eTABLEfoot
+\stopxmlsetups
+
+\startxmlsetups xml:html:tr
+ \bTR[ny=\xmlattdef{#1}{rowspan}{1}]
+ \xmlflush{#1}
+ \eTR
+\stopxmlsetups
+
+\startxmlsetups xml:html:td
+ \bTD[nx=\xmlattdef{#1}{colspan}{1}]
+ \xmlflush{#1}
+ \eTD
+\stopxmlsetups
+
+\startxmlsetups xml:html:th
+ \bTH[nx=\xmlattdef{#1}{colspan}{1}]
+ \xmlflush{#1}
+ \eTH
+\stopxmlsetups
+
+% \xmlregistersetup{xml:html:basics}
+
+%D For old times sake:
+
+\startxmlsetups xml:setups:common
+ \xmlsetup{#1}{xml:html:basics}
+ \xmlsetup{#1}{xml:html:tables}
+% \ifconditional\qmlcleanuptwo
+% \xmlsetsetup{#1}{html/br[index() == 1]}{xml:noppes:1}
+% \xmlsetsetup{#1}{html/p[index() == lastindex()]/br[index() == lastindex()]}{xml:noppes:2}
+% \xmlsetsetup{#1}{html/br[index() == lastindex()]}{xml:noppes:3}
+% \xmlsetsetup{#1}{br[name(1) == 'img']}{xml:noppes}
+% \xmlsetsetup{#1}{br[name(1) == 'br' and name(2) == 'img']}{xml:noppes}
+% % \xmlsetsetup{#1}{br/following-sibling::img[position()==1]}{xml:noppes}
+% \fi
+\stopxmlsetups
+
+\stopmodule
+
+\continueifinputfile{x-html.mkiv}
+
+\xmlregistersetup{xml:html:basics}
+\xmlregistersetup{xml:html:tables}
+
+\startxmlsetups xml:whatever
+ \xmlsetsetup {#1} {
+ html|body
+ } {xml:html:*}
+\stopxmlsetups
+
+\xmlregisterdocumentsetup{main}{xml:whatever}
+
+\startxmlsetups xml:html:html
+ \xmlflush{#1}
+\stopxmlsetups
+
+\startxmlsetups xml:html:body
+ \xmlflush{#1}
+\stopxmlsetups
+
+\setuphead[subject][page=yes,style=\bfa]
+
+\starttexdefinition ShowExample#1
+ \startsubject[title=#1]
+ \typebuffer[#1]
+ \starttextrule{result}
+ \xmlprocessbuffer{main}{#1}{}
+ \stoptextrule
+ \stopsubject
+\stoptexdefinition
+
+\starttext
+
+\startbuffer[test 1]
+<html><body>
+<p>test</p>
+<p/>
+<p>test</p>
+</body></html>
+\stopbuffer
+
+\startbuffer[test 2]
+<html><body>
+<p>test (hierna een lf)
+test</p>
+</body></html>
+\stopbuffer
+
+\startbuffer[test 3]
+<html><body>
+<p>test (hierna een lf met lege regel)
+
+test</p>
+</body></html>
+\stopbuffer
+
+\startbuffer[test 4]
+<html><body>
+<p>test (hierna een lf met twee lege regels)
+
+
+test</p>
+</body></html>
+\stopbuffer
+
+\startbuffer[test 5]
+<html><body>
+<p>test (hierna br geen lf)<br/> test</p>
+</body></html>
+\stopbuffer
+
+\startbuffer[test 6]
+<html><body>
+<p>test (hierna br met lf)<br/>
+test</p>
+</body></html>
+\stopbuffer
+
+\startbuffer[test 7]
+<html><body>
+<p>test (hierna br met lf en lege regel)<br/>
+
+test</p>
+</body></html>
+\stopbuffer
+
+\startbuffer[test 8]
+<html><body>
+<p>test (hierna br met lf en twee lege regels)<br/>
+
+
+test</p>
+</body></html>
+\stopbuffer
+
+\startbuffer[test 9]
+<html><body>
+<p>test (hierna bold) <b>bold</b> test</p>
+</body></html>
+\stopbuffer
+
+\startbuffer[test 10]
+<html><body>
+<p>test (hierna lf met bold)
+<b>bold <u>underlined</u></b> test</p>
+</body></html>
+\stopbuffer
+
+\startbuffer[test 11]
+<html><body>
+<p>test (hierna lf met lege regel en bold)
+
+<b>bold</b> test</p>
+</body></html>
+\stopbuffer
+
+\startbuffer[test 12]
+<html><body>
+<p>test (hierna lf met lege regel en lf in bold)
+
+<b>
+bold
+</b> test</p>
+</body></html>
+\stopbuffer
+
+\startbuffer[test 13]
+<html><body>
+<p>test (hierna lf met lege regel en lf en lege regel in bold)
+
+<b>
+
+bold
+
+</b> test</p>
+</body></html>
+\stopbuffer
+
+\dorecurse{13}{\ShowExample{test #1}}
+
+\stoptext
diff --git a/tex/context/modules/mkiv/x-ldx.ctx b/tex/context/modules/mkiv/x-ldx.ctx
new file mode 100644
index 000000000..edbffc285
--- /dev/null
+++ b/tex/context/modules/mkiv/x-ldx.ctx
@@ -0,0 +1,23 @@
+<?xml version='1.0' standalone='yes'?>
+
+<ctx:job>
+ <ctx:message>Lua Documentation Generator</ctx:message>
+ <ctx:preprocess>
+ <ctx:processors>
+ <ctx:processor name='ldx'>mtxrun --script x-ldx.lua <ctx:value name='old'/> <ctx:value name='new'/></ctx:processor>
+ </ctx:processors>
+ <ctx:files>
+ <ctx:file processor='ldx'><ctx:value name='old'/></ctx:file>
+ </ctx:files>
+ </ctx:preprocess>
+ <ctx:flags>
+ <ctx:flag>purge</ctx:flag>
+ <ctx:flag>forcexml</ctx:flag>
+ </ctx:flags>
+ <ctx:process>
+ <ctx:resources>
+ <ctx:environment>x-ldx.mkiv</ctx:environment>
+ </ctx:resources>
+ </ctx:process>
+</ctx:job>
+
diff --git a/tex/context/modules/mkiv/x-ldx.lua b/tex/context/modules/mkiv/x-ldx.lua
new file mode 100644
index 000000000..31cbebf13
--- /dev/null
+++ b/tex/context/modules/mkiv/x-ldx.lua
@@ -0,0 +1,341 @@
+if not modules then modules = { } end modules ['x-ldx'] = {
+ version = 1.001,
+ comment = "companion to x-ldx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- --[[ldx--
+-- <topic>Introduction</topic>
+-- --ldx]]--
+
+--[[ldx--
+<source>Lua Documentation Module</source>
+
+This file is part of the <logo label='context'/> documentation suite and
+itself serves as an example of using <logo label='lua'/> in combination
+with <logo label='tex'/>.
+
+I will rewrite this using lpeg. On the other hand, we cannot expect proper
+<logo label='tex'/> and for educational purposed the syntax might be wrong.
+--ldx]]--
+
+-- there is a nice parser on from http://lua-users.org/wiki/LpegRecipes (by
+-- Patrick Donnelly) but lua crashes when I apply functions to some of the
+-- matches
+
+banner = "version 1.0.1 - 2007+ - PRAGMA ADE / CONTEXT"
+
+--[[
+This script needs a few libraries. Instead of merging the code here
+we can use
+
+<typing>
+mtxrun --internal x-ldx.lua
+</typing>
+
+That way, the libraries included in the runner will be used.
+]]--
+
+-- libraries l-string.lua l-table.lua l-io.lua l-file.lua
+
+-- begin library merge
+-- end library merge
+
+local gsub, find, sub = string.gsub, string.find, string.sub
+local splitstring, emptystring = string.split, string.is_empty
+local concat = table.concat
+
+--[[
+Just a demo comment line. We will handle such multiline comments but
+only when they start and end at the beginning of a line. More rich
+comments are tagged differently.
+]]--
+
+--[[ldx--
+First we define a proper namespace for this module. The <q>l</q> stands for
+<logo label='lua'/>, the <q>d</q> for documentation and the <q>x</q> for
+<logo label='xml'/>.
+--ldx]]--
+
+if not ldx then ldx = { } end
+
+--[[ldx--
+We load the lua file into a table. The entries in this table themselves are
+tables and have keys like <t>code</t> and <t>comment</t>.
+--ldx]]--
+
+function ldx.load(filename)
+ local data = file.readdata(filename)
+ local expr = "%s*%-%-%[%[ldx%-*%s*(.-)%s*%-%-ldx%]%]%-*%s*"
+ local i, j, t = 0, 0, { }
+ while true do
+ local comment, ni
+ ni, j, comment = find(data, expr, j)
+ if not ni then break end
+ t[#t+1] = { code = sub(data, i, ni-1) }
+ t[#t+1] = { comment = comment }
+ i = j + 1
+ end
+ local str = sub(data, i, #data)
+ str = gsub(str, "^%s*(.-)%s*$", "%1")
+ if #str > 0 then
+ t[#t+1] = { code = str }
+ end
+ return t
+end
+
+--[[ldx--
+We will tag keywords so that we can higlight them using a special font
+or color. Users can extend this list when needed.
+--ldx]]--
+
+ldx.keywords = { }
+
+--[[ldx--
+Here come the reserved words:
+--ldx]]--
+
+ldx.keywords.reserved = {
+ ["and"] = 1,
+ ["break"] = 1,
+ ["do"] = 1,
+ ["else"] = 1,
+ ["elseif"] = 1,
+ ["end"] = 1,
+ ["false"] = 1,
+ ["for"] = 1,
+ ["function"] = 1,
+ ["if"] = 1,
+ ["in"] = 1,
+ ["local"] = 1,
+ ["nil"] = 1,
+ ["not"] = 1,
+ ["or"] = 1,
+ ["repeat"] = 1,
+ ["return"] = 1,
+ ["then"] = 1,
+ ["true"] = 1,
+ ["until"] = 1,
+ ["while"] = 1
+}
+
+--[[ldx--
+We need to escape a few tokens. We keep the hash local to the
+definition but set it up only once, hence the <key>do</key>
+construction.
+--ldx]]--
+
+do
+ local e = { [">"] = "&gt;", ["<"] = "&lt;", ["&"] = "&amp;" }
+ function ldx.escape(str)
+ return (gsub(str, "([><&])",e))
+ end
+end
+
+--[[ldx--
+Enhancing the code is a bit tricky due to the fact that we have to
+deal with strings and escaped quotes within these strings. Before we
+mess around with the code, we hide the strings, and after that we
+insert them again. Single and double quoted strings are tagged so
+that we can use a different font to highlight them.
+--ldx]]--
+
+ldx.make_index = true
+
+function ldx.enhance(data) -- i need to use lpeg and then we can properly autoindent -)
+ local e = ldx.escape
+ for k=1,#data do
+ local v = data[k]
+ if v.code then
+ local dqs, sqs, com, cmt, cod = { }, { }, { }, { }, e(v.code)
+ cod = gsub(cod, '\\"', "##d##")
+ cod = gsub(cod, "\\'", "##s##")
+ cod = gsub(cod, "%-%-%[%[.-%]%]%-%-", function(s)
+ cmt[#cmt+1] = s
+ return "<l<<<".. #cmt ..">>>l>"
+ end)
+ cod = gsub(cod, "%-%-([^\n]*)", function(s)
+ com[#com+1] = s
+ return "<c<<<".. #com ..">>>c>"
+ end)
+ cod = gsub(cod, "(%b\"\")", function(s)
+ dqs[#dqs+1] = sub(s,2,-2) or ""
+ return "<d<<<".. #dqs ..">>>d>"
+ end)
+ cod = gsub(cod, "(%b\'\')", function(s)
+ sqs[#sqs+1] = sub(s,2,-2) or ""
+ return "<s<<<".. #sqs ..">>>s>"
+ end)
+ cod = gsub(cod, "(%a+)",function(key)
+ local class = ldx.keywords.reserved[key]
+ if class then
+ return "<key class='" .. class .. "'>" .. key .. "</key>"
+ else
+ return key
+ end
+ end)
+ cod = gsub(cod, "<s<<<(%d+)>>>s>", function(s)
+ return "<sqs>" .. sqs[tonumber(s)] .. "</sqs>"
+ end)
+ cod = gsub(cod, "<d<<<(%d+)>>>d>", function(s)
+ return "<dqs>" .. dqs[tonumber(s)] .. "</dqs>"
+ end)
+ cod = gsub(cod, "<c<<<(%d+)>>>c>", function(s)
+ return "<com>" .. com[tonumber(s)] .. "</com>"
+ end)
+ cod = gsub(cod, "<l<<<(%d+)>>>l>", function(s)
+ return cmt[tonumber(s)]
+ end)
+ cod = gsub(cod, "##d##", "\\\"")
+ cod = gsub(cod, "##s##", "\\\'")
+ if ldx.make_index then
+ local lines = splitstring(cod,"\n")
+ local f = "(<key class='1'>function</key>)%s+([%w%.]+)%s*%("
+ for k=1,#lines do
+ local v = lines[k]
+ -- functies
+ v = gsub(v,f,function(key, str)
+ return "<function>" .. str .. "</function>("
+ end)
+ -- variables
+ v = gsub(v,"^([%w][%w%,%s]-)(=[^=])",function(str, rest)
+ local t = splitstring(str,",%s*")
+ for k=1,#t do
+ t[k] = "<variable>" .. t[k] .. "</variable>"
+ end
+ return concat(t,", ") .. rest
+ end)
+ -- so far
+ lines[k] = v
+ end
+ v.code = concat(lines,"\n")
+ else
+ v.code = cod
+ end
+ end
+ end
+end
+
+--[[ldx--
+We're now ready to save the file in <logo label='xml'/> format. This boils
+down to wrapping the code and comment as well as the whole document. We tag
+lines in the code as such so that we don't need messy <t>CDATA</t> constructs
+and by calculating the indentation we also avoid space troubles. It also makes
+it possible to change the indentation afterwards.
+--ldx]]--
+
+function ldx.as_xml(data) -- ldx: not needed
+ local t, cmode = { }, false
+ t[#t+1] = "<?xml version='1.0' standalone='yes'?>\n"
+ t[#t+1] = "\n<document xmlns:ldx='http://www.pragma-ade.com/schemas/ldx.rng' xmlns='http://www.pragma-ade.com/schemas/ldx.rng'>\n"
+ for k=1,#data do
+ local v = data[k]
+ if v.code and not emptystring(v.code) then
+ t[#t+1] = "\n<code>\n"
+ local split = splitstring(v.code,"\n")
+ for k=1,#split do -- make this faster
+ local v = split[k]
+ local a, b = find(v,"^(%s+)")
+ if v then v = gsub(v,"[\n\r ]+$","") end
+ if a and b then
+ v = sub(v,b+1,#v)
+ if cmode then
+ t[#t+1] = "<line comment='yes' n='" .. b .. "'>" .. v .. "</line>\n"
+ else
+ t[#t+1] = "<line n='" .. b .. "'>" .. v .. "</line>\n"
+ end
+ elseif emptystring(v) then
+ if cmode then
+ t[#t+1] = "<line comment='yes'/>\n"
+ else
+ t[#t+1] = "<line/>\n"
+ end
+ elseif find(v,"^%-%-%[%[") then
+ t[#t+1] = "<line comment='yes'>" .. v .. "</line>\n"
+ cmode= true
+ elseif find(v,"^%]%]%-%-") then
+ t[#t+1] = "<line comment='yes'>" .. v .. "</line>\n"
+ cmode= false
+ elseif cmode then
+ t[#t+1] = "<line comment='yes'>" .. v .. "</line>\n"
+ else
+ t[#t+1] = "<line>" .. v .. "</line>\n"
+ end
+ end
+ t[#t+1] = "</code>\n"
+ elseif v.comment then
+ t[#t+1] = "\n<comment>\n" .. v.comment .. "\n</comment>\n"
+ else
+ -- cannot happen
+ end
+ end
+ t[#t+1] = "\n</document>\n"
+ return concat(t,"")
+end
+
+--[[ldx--
+Saving the result is a trivial effort.
+--ldx]]--
+
+function ldx.save(filename,data)
+ file.savedata(filename,ldx.as_xml(data))
+end
+
+--[[ldx--
+The next function wraps it all in one call:
+--ldx]]--
+
+function ldx.convert(luaname,ldxname)
+ if not file.is_readable(luaname) then
+ luaname = luaname .. ".lua"
+ end
+ if file.is_readable(luaname) then
+ if not ldxname then
+ ldxname = file.replacesuffix(luaname,"ldx")
+ end
+ local data = ldx.load(luaname)
+ if data then
+ ldx.enhance(data)
+ if ldxname ~= luaname then
+ ldx.save(ldxname,data)
+ end
+ end
+ end
+end
+
+--[[ldx--
+This module can be used directly:
+
+<typing>
+mtxrun --internal x-ldx somefile.lua
+</typing>
+
+will produce an ldx file that can be processed with <logo label='context'/>
+by running:
+
+<typing>
+context --use=x-ldx --forcexml somefile.ldx
+</typing>
+
+You can do this in one step by saying:
+
+<typing>
+context --ctx=x-ldx somefile.lua
+</typing>
+
+This will trigger <logo label='context'/> into loading the mentioned
+<logo label='ctx'/> file. That file describes the conversion as well
+as the module to be used.
+
+The main conversion call is:
+--ldx]]--
+
+-- todo: assume usage of "mtxrun --script x-ldx", maybe make it mtx-ldx
+
+if environment.files and environment.files[1] then
+ ldx.convert(environment.files[1],environment.files[2])
+end
+
+--~ exit(1)
diff --git a/tex/context/modules/mkiv/x-ldx.mkiv b/tex/context/modules/mkiv/x-ldx.mkiv
new file mode 100644
index 000000000..0156f2c55
--- /dev/null
+++ b/tex/context/modules/mkiv/x-ldx.mkiv
@@ -0,0 +1,196 @@
+%D \module
+%D [ file=x-ldx,
+%D version=2008.06.03,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Lua Source Pretty Printing,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% this will become an extra
+
+\setupxml[default=hidden]
+
+\usemodule[x][mathml]
+\usemodule[abr-02]
+
+\xmlregistersetup{xml:mml:define}
+\xmlregistersetup{xml:ldx:define}
+
+\xmlregisterns{ldx}{ldx}
+
+\startxmlsetups xml:ldx:define
+ \xmlsetsetup {#1} {ldx:*} {ldx:*}
+\stopxmlsetups
+
+% % %
+
+\startxmlsetups ldx:p
+ \xmlflush{#1}\par
+\stopxmlsetups
+
+\startxmlsetups ldx:source
+ \source{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups ldx:key
+ \dontleavehmode{\bf\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups ldx:variable
+ \xmlflush{#1}
+% \expanded{\variable{\xmlflush{#1}}}
+\stopxmlsetups
+
+\startxmlsetups ldx:function
+ \dontleavehmode{\bf function}\space\xmlflush{#1}
+% \expanded{\function{\xmlflush{#1}}}
+\stopxmlsetups
+
+\startxmlsetups ldx:com
+ \dontleavehmode{\tt--\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups ldx:document
+ \page
+ \xmlflush{#1}
+ \determineregistercharacteristics[function]
+ \startmode[*register]
+ \testpage[4]
+ \extra{Functions}
+ \placeregister[function]
+ \stopmode
+ \determineregistercharacteristics[variable]
+ \startmode[*register]
+ \testpage[4]
+ \extra{Variables}
+ \placeregister[variable]
+ \stopmode
+\stopxmlsetups
+
+\newcounter\CommentCounter
+
+\startxmlsetups ldx:comment
+ \blank
+ \doglobal\increment\CommentCounter
+ \margintitle{\bf\CommentCounter}
+ \xmlflush{#1}
+ \blank
+\stopxmlsetups
+
+\startxmlsetups ldx:dqs
+ \dontleavehmode\bgroup\tt"\xmlflush{#1}"\egroup
+\stopxmlsetups
+
+\startxmlsetups ldx:sqs
+ \dontleavehmode\bgroup\tt'\xmlflush{#1}'\egroup
+\stopxmlsetups
+
+\startxmlsetups ldx:code
+ \startpacked
+ \xmlflush{#1}\relax
+ \stoppacked
+\stopxmlsetups
+
+\startxmlsetups ldx:lines
+ \startpacked
+ \xmlflush{#1}
+ \stoppacked
+\stopxmlsetups
+
+\startxmlsetups ldx:line
+ \doifelsenothing {\xmlflush{#1}} {
+ \xmlflush{#1}\crlf
+ } {
+ \dontleavehmode \hbox to \hsize \bgroup
+ \strut
+ \hskip.25\dimexpr\xmlattdef{#1}{n}{0}em\relax\relax % extra relax needed !
+ \doif {\xmlatt{#1}{comment}} {yes} {\tt}
+ \xmlflush{#1}
+ \hss
+ \egroup
+ \endgraf
+ }
+\stopxmlsetups
+
+\startxmlsetups ldx:logo
+ \uppercasestring\xmlatt{#1}{label}\xmlatt{#1}{name}\to\ascii
+ \ifx\ascii\empty\else\getvalue{\ascii}\fi
+\stopxmlsetups
+
+\startxmlsetups ldx:l
+ \uppercasestring\xmlatt{#1}{l}\xmlatt{#1}{n}\to\ascii
+ \ifx\ascii\empty\else\getvalue{\ascii}\fi
+\stopxmlsetups
+
+\startxmlsetups ldx:typing
+ \blank
+ \startpacked \tt
+ \xmlverbatim{#1}
+ \stoppacked
+ \blank
+\stopxmlsetups
+
+\startxmlsetups ldx:type
+ \dontleavehmode{\tt\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups ldx:t
+ \dontleavehmode{\tt\xmlflush{#1}}
+\stopxmlsetups
+
+% key -> kw
+% dqs -> dq
+% sqs -> sq
+% line -> ln
+% code -> cd
+% comment -> tx (text)
+
+\definetypeface[mainfacenormal] [ss][sans] [iwona] [default]
+\definetypeface[mainfacenormal] [rm][serif][palatino] [default]
+\definetypeface[mainfacenormal] [tt][mono] [modern] [default][rscale=1] % 1.1
+\definetypeface[mainfacenormal] [mm][math] [iwona] [default][encoding=default]
+
+\definetypeface[mainfacemedium] [ss][sans] [iwona-medium][default]
+\definetypeface[mainfacenormal] [rm][serif][palatino] [default]
+\definetypeface[mainfacemedium] [tt][mono] [modern] [default][rscale=1] % 1.1
+\definetypeface[mainfacemedium] [mm][math] [iwona-medium][default][encoding=default]
+
+\definetypeface[mainfacenarrowtt][tt][mono] [modern-cond] [default][rscale=1] % 1.1
+
+\setupbodyfont
+ [mainfacenormal,11pt]
+
+\setupwhitespace
+ [big]
+
+\defineregister[function][functions]
+\defineregister[variable][variables]
+
+\definehead[source][subject]
+\definehead[extra] [subsubject]
+\definehead[topic] [subsubsubject]
+
+\setuphead
+ [source]
+ [style=\bfb]
+
+\setuphead
+ [extra]
+ [style=\bfa]
+
+\setuphead
+ [topic]
+ [style=\bf]
+
+\setuplayout
+ [width=middle,
+ height=middle,
+ backspace=2cm,
+ topspace=2cm]
+
+\endinput
diff --git a/tex/context/modules/mkiv/x-math-svg.lua b/tex/context/modules/mkiv/x-math-svg.lua
new file mode 100644
index 000000000..8a6288167
--- /dev/null
+++ b/tex/context/modules/mkiv/x-math-svg.lua
@@ -0,0 +1,176 @@
+if not modules then modules = { } end modules ['x-math-svg'] = {
+ version = 1.001,
+ comment = "companion to x-math-svg.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local tostring, type, next = tostring, type, next
+local lpegmatch, P, Cs = lpeg.match, lpeg.P, lpeg.Cs
+
+local xmlfirst = xml.first
+local xmlconvert = xml.convert
+local xmlload = xml.load
+local xmlsave = xml.save
+local xmlcollected = xml.collected
+local xmldelete = xml.delete
+
+local loadtable = table.load
+local savetable = table.save
+
+local replacesuffix = file.replacesuffix
+local addsuffix = file.addsuffix
+local removefile = os.remove
+local isfile = lfs.isfile
+
+local formatters = string.formatters
+
+moduledata = moduledata or table.setmetatableindex("table")
+local svgmath = moduledata.svgmath -- autodefined
+
+local namedata = { }
+local pagedata = { }
+
+local statusname = "x-math-svg-status.lua"
+local pdfname = "x-math-svg.pdf"
+
+local pdftosvg = os.which("mudraw")
+
+local f_make_tex = formatters[ [[context --global kpse:x-math-svg.mkvi --inputfile="%s" --svgstyle="%s" --batch --noconsole --once --purgeall]] ]
+local f_make_svg = formatters[ [[mudraw -o "math-%%d.svg" "%s" 1-9999]] ]
+
+----- f_inline = formatters[ [[<div class='math-inline' style='vertical-align:%p'></div>]] ]
+local f_inline = formatters[ [[<div class='math-inline'></div>]] ]
+local f_display = formatters[ [[<div class='math-display'></div>]] ]
+local f_style = formatters[ [[vertical-align:%p]] ]
+
+local f_math_tmp = formatters[ [[math-%i]] ]
+
+function svgmath.process(filename)
+ if not filename then
+ -- no filename given
+ return
+ elseif not isfile(filename) then
+ -- invalid filename
+ return
+ end
+ local index = 0
+ local page = 0
+ local blobs = { }
+ local root = xmlload(filename)
+ for mth in xmlcollected(root,"math") do
+ index = index + 1
+ local blob = tostring(mth)
+ if blobs[blob] then
+ context.ReuseSVGMath(index,blobs[blob])
+ else
+ page = page + 1
+ buffers.assign(f_math_tmp(page),blob)
+ context.MakeSVGMath(index,page,mth.at.display)
+ blobs[blob] = page
+ end
+ end
+ context(function()
+ -- for tracing purposes:
+ for mathdata, pagenumber in next, blobs do
+ local p = pagedata[pagenumber]
+ p.mathml = mathdata
+ p.number = pagenumber
+ end
+ --
+ savetable(statusname, {
+ pagedata = pagedata,
+ namedata = namedata,
+ })
+ end)
+end
+
+function svgmath.register(index,page,specification)
+ if specification then
+ pagedata[page] = specification
+ end
+ namedata[index] = page
+end
+
+function svgmath.convert(filename,svgstyle)
+ if not filename then
+ -- no filename given
+ return false, "no filename"
+ elseif not isfile(filename) then
+ -- invalid filename
+ return false, "invalid filename"
+ elseif not pdftosvg then
+ return false, "mudraw is not installed"
+ end
+
+ os.execute(f_make_tex(filename,svgstyle))
+
+ local data = loadtable(statusname)
+ if not data then
+ -- invalid tex run
+ return false, "invalid tex run"
+ elseif not next(data) then
+ return false, "no converson needed"
+ end
+
+ local pagedata = data.pagedata
+ local namedata = data.namedata
+
+ os.execute(f_make_svg(pdfname))
+
+ local root = xmlload(filename)
+ local index = 0
+ local done = { }
+ local unique = 0
+
+ local between = (1-P("<"))^1/""
+ local strip = Cs((
+ (P("<text") * ((1-P("</text>"))^1) * P("</text>")) * between^0 / "" +
+ P(">") * between +
+ P(1)
+ )^1)
+
+ for mth in xmlcollected(root,"m:math") do
+ index = index + 1
+ local page = namedata[index]
+ if done[page] then
+ mth.__p__.dt[mth.ni] = done[page]
+ else
+ local info = pagedata[page]
+ local depth = info.depth
+ local mode = info.mode
+ local svgname = addsuffix(f_math_tmp(page),"svg")
+ local action = mode == "inline" and f_inline or f_display
+ -- local x_div = xmlfirst(xmlconvert(action(-depth)),"/div")
+ local x_div = xmlfirst(xmlconvert(action()),"/div")
+ local svgdata = io.loaddata(svgname)
+ if not svgdata or svgdata == "" then
+ print("error in:",svgname,tostring(mth))
+ else
+ -- svgdata = string.gsub(svgdata,">%s<","")
+ svgdata = lpegmatch(strip,svgdata)
+ local x_svg = xmlfirst(xmlconvert(svgdata),"/svg")
+ -- xmldelete(x_svg,"text")
+if mode == "inline" then
+ x_svg.at.style = f_style(-depth)
+end
+
+ x_div.dt = { x_svg }
+ mth.__p__.dt[mth.ni] = x_div -- use helper
+ end
+ done[page] = x_div
+ unique = unique + 1
+ end
+ end
+
+-- for k, v in next, data do
+-- removefile(addsuffix(k,"svg"))
+-- end
+-- removefile(statusname)
+-- removefile(pdfname)
+
+ xmlsave(root,filename)
+
+ return true, index, unique
+end
diff --git a/tex/context/modules/mkiv/x-mathml-basics.mkiv b/tex/context/modules/mkiv/x-mathml-basics.mkiv
new file mode 100644
index 000000000..e166995b0
--- /dev/null
+++ b/tex/context/modules/mkiv/x-mathml-basics.mkiv
@@ -0,0 +1,276 @@
+% macros=mkvi
+
+% makes sense (but rel vs op ...):
+
+% \unexpanded\def\stackrel#1#2{\mathematics{\mathop{\let\limits\relax\mover{#2}{#1}}}}
+
+% this can become a core helper
+
+% bwe could do all of them in lua
+
+\startluacode
+local find = string.find
+local lpegmatch = lpeg.match
+
+local splitter = lpeg.Ct(lpeg.C(lpeg.patterns.nestedbraces + lpeg.patterns.utf8character)^1)
+
+function commands.xmfenced(left,middle,right,content)
+ local l = left ~= "" and left or "("
+ local r = right ~= "" and right or ")"
+ local m = middle ~= "" and middle and lpegmatch(splitter,middle) or { "," }
+ local c = find(content,"{") and lpegmatch(splitter,content) or { content }
+ local n = #c
+ if n > 1 then
+ context("\\left%s",l)
+ for i=1,n do
+ if i > 1 then
+ context("%s %s",m[i] or m[#m],c[i])
+ else
+ context(c[i])
+ end
+ end
+ context("\\right%s",r)
+ else
+ context("\\left%s %s \\right%s",l,content,r)
+ end
+end
+
+\stopluacode
+
+\unprotect
+
+\unexpanded\def\mexecuteifdefined#1%
+ {\ifx#1\empty
+ \expandafter\secondoftwoarguments
+ \else\ifcsname#1\endcsname
+ \doubleexpandafter\firstoftwoarguments
+ \else
+ \doubleexpandafter\secondoftwoarguments
+ \fi\fi
+ {\csname#1\endcsname}}
+
+% mrow
+
+\let\mrow\mathematics
+
+% msub msup msubsup
+
+\starttexdefinition msub #1#2
+ \mathematics {
+ #1_{#2}
+ }
+\stoptexdefinition
+
+\starttexdefinition msup #1#2
+ \mathematics {
+ #1^{#2}
+ }
+\stoptexdefinition
+
+\starttexdefinition msubsup #1#2#3
+ \mathematics {
+ #1_{#2}^{#3}
+ }
+\stoptexdefinition
+
+% mn mo mi
+
+\let\mn\mathematics
+\let\mo\mathematics
+\let\mi\mathematics
+
+% ms mtext
+
+\starttexdefinition ms #1
+ \text {
+ "#1"
+ }
+\stoptexdefinition
+
+\starttexdefinition mtext #1
+ \text {
+ #1
+ }
+\stoptexdefinition
+
+% mover
+
+\starttexdefinition unexpanded moverabove #1
+ \edef\movercommand{\utfmathfiller\movertoken}
+ \mexecuteifdefined\movercommand {#1} \relax
+\stoptexdefinition
+\starttexdefinition unexpanded moverbase #1
+ \edef\mbasecommand{\utfmathfiller\mbasetoken}
+ \mexecuteifdefined\mbasecommand {#1}
+ \relax
+\stoptexdefinition
+\starttexdefinition unexpanded moverbasefiller #1#2
+ \edef\mbasecommand{e\utfmathcommandfiller\mbasetoken}
+ \mexecuteifdefined\mbasecommand \relax {#2} {}
+\stoptexdefinition
+\starttexdefinition unexpanded moveraccent #1#2
+ \edef\movercommand{\utfmathcommandabove\movertoken}
+ \mexecuteifdefined\movercommand \relax {#1}
+\stoptexdefinition
+\starttexdefinition unexpanded movertext #1#2
+ % \mathtriplet {\mathstylehbox{#1}} {#2} {}
+ \mathtriplet {\mathematics{#1}} {#2} {}
+\stoptexdefinition
+\starttexdefinition unexpanded moveraccentchecker #1#2
+ \edef\movertoken{\tochar{#2}}
+ \doifelseutfmathabove\movertoken \moveraccent \movertext {#1}{#2}
+\stoptexdefinition
+
+\starttexdefinition unexpanded mover #1#2
+ \mathematics {
+ \edef\mbasetoken{\tochar{#1}}
+ \doifelseutfmathfiller\mbasetoken \moverbasefiller \moveraccentchecker {#1}{#2}
+ }
+\stoptexdefinition
+
+% munder
+
+\starttexdefinition unexpanded munderbelow #1
+ \edef\mundercommand{\utfmathfiller\mundertoken}
+ \mexecuteifdefined\mundercommand {#1} \relax
+\stoptexdefinition
+\starttexdefinition unexpanded munderbase #1
+ \edef\mbasecommand{\utfmathfiller\mbasetoken}
+ \mexecuteifdefined\mbasecommand {#1}
+ \relax
+\stoptexdefinition
+\starttexdefinition unexpanded munderbasefiller #1#2
+ \edef\mbasecommand{e\utfmathcommandfiller\mbasetoken}
+ \mexecuteifdefined\mbasecommand \relax {#2} {}
+\stoptexdefinition
+\starttexdefinition unexpanded munderaccent #1#2
+ \edef\mundercommand{\utfmathcommandbelow\mundertoken}
+ \mexecuteifdefined\mundercommand \relax {#1}
+\stoptexdefinition
+\starttexdefinition unexpanded mundertext #1#2
+ % \mathtriplet {\mathstylehbox{#1}} {} {#2}
+ \mathtriplet {\mathematics{#1}} {} {#2}
+\stoptexdefinition
+\starttexdefinition unexpanded munderaccentchecker #1#2
+ \edef\mundertoken{\tochar{#2}}
+ \doifelseutfmathbelow\mundertoken \munderaccent \mundertext {#1}{#2}
+\stoptexdefinition
+
+\starttexdefinition unexpanded munder #1#2
+ \mathematics {
+ \edef\mbasetoken{\tochar{#1}}
+ \doifelseutfmathfiller\mbasetoken \munderbasefiller \munderaccentchecker {#1}{#2}
+ }
+\stoptexdefinition
+
+% munderover
+
+% mfenced
+
+% \mfenced{x,y}
+% \mfenced{{x}{y}}
+% \mfenced[separators]{{x}{y}}
+% \mfenced[left][right]{{x}{y}}
+% \mfenced[left][separators][right]{{x}{y}}
+
+\starttexdefinition unexpanded mfenced
+ \dotripleempty\do_mfenced
+\stoptexdefinition
+
+\starttexdefinition unexpanded do_mfenced [#1][#2][#3]#4
+ \mathematics {
+ \ctxcommand{xmfenced(
+ \ifthirdargument "#1","#2","#3"\else
+ \ifsecondargument "#1",",","#2"\else
+ \iffirstargument "(","#1",")"\else
+ "(",",",")"\fi\fi\fi
+ ,"#4")}
+ }
+\stoptexdefinition
+
+% mfrac
+
+\starttexdefinition unexpanded mfrac #1#2
+ \mathematics {
+ \frac{#1}{#2}
+ }
+\stoptexdefinition
+
+% mroot msqrt
+
+\starttexdefinition unexpanded mroot #1#2
+ \mathematics {
+ \sqrt[#1]{#2}
+ }
+\stoptexdefinition
+
+\starttexdefinition unexpanded msqrt #1
+ \mathematics {
+ \sqrt{#1}
+ }
+\stoptexdefinition
+
+% menclose
+
+% merror
+
+% mglyph
+
+% mmultiscripts
+
+% mpadded
+
+% mphantom
+
+% mspace
+
+% mstyle
+
+% mtable mtr mlabeledtr mtd
+
+% maction
+
+% semantics
+
+\protect
+
+\continueifinputfile{x-mathml-basics.mkiv}
+
+\starttext
+
+$\mfenced{1+a}$\par
+$\mfenced[,]{1+a}$\par
+$\mfenced[,]{{1+a}{1+b}}$\par
+
+% $\mover{←}{test}$\par
+% $\mover{\utfchar{"2190}}{test}$\par
+% $\mover{e:leftarrow}{test}$\par
+% $\mover{x:2190}{test}$\par
+
+% $\mover{test}{⏞}$\par
+% $\mover{test}{\utfchar{"23DE}}$\par
+% $\mover{test}{e:overbrace}$\par
+% $\mover{test}{x:23DE}$\par
+% $\mover{test}{over}$\par
+
+% \mover{test}{⏞}\par
+% \mover{test}{\utfchar{"23DE}}\par
+% \mover{test}{e:overbrace}\par
+% \mover{test}{x:23DE}\par
+
+% $\munder{←}{test}$\par
+% $\munder{\utfchar{"2190}}{test}$\par
+% $\munder{e:leftarrow}{test}$\par
+% $\munder{x:2190}{test}$\par
+
+% $\munder{test}{⏟}$\par
+% $\munder{test}{\utfchar{"23DF}}$\par
+% $\munder{test}{e:underbrace}$\par
+% $\munder{test}{x:23DF}$\par
+% $\munder{test}{under}$\par
+
+% \math{{\msup{x}{2}\mo{+}\mn{2}\mi{x}\mo{+}\mi{b}}}
+
+% \mrow{\msup{x}{2}\mo{+}\mn{2}\mi{x}\mo{+}\mi{b}}
+
+\stoptext
diff --git a/tex/context/modules/mkiv/x-mathml-html.mkiv b/tex/context/modules/mkiv/x-mathml-html.mkiv
new file mode 100644
index 000000000..2ac7d3cba
--- /dev/null
+++ b/tex/context/modules/mkiv/x-mathml-html.mkiv
@@ -0,0 +1,40 @@
+%D \modul
+%D [ file=x-mathml,
+%D version=2014.05.18,
+%D title=\CONTEXT\ XML Modules,
+%D subtitle=\MATHML\ embedded HTML,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% maybe some more
+
+\startmodule [mathml-html]
+
+\startxmlsetups mml:html:b
+ \bold{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups mml:html:i
+ \italic{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups mml:html:tt
+ \mono{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups mml:html:em
+ \emphasized{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups mml:html
+ \xmlsetsetup{#1}{mml:b|mml:i|mml:tt|mml:em}{mml:html:*}
+\stopxmlsetups
+
+\xmlregistersetup{mml:html}
+
+\stopmodule
diff --git a/tex/context/modules/mkiv/x-mathml.lua b/tex/context/modules/mkiv/x-mathml.lua
new file mode 100644
index 000000000..50369407f
--- /dev/null
+++ b/tex/context/modules/mkiv/x-mathml.lua
@@ -0,0 +1,898 @@
+if not modules then modules = { } end modules ['x-mathml'] = {
+ version = 1.001,
+ comment = "companion to x-mathml.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This needs an upgrade to the latest greatest mechanisms. But ... it
+-- probably doesn't pay back as no mathml support ever did.
+
+local type, next = type, next
+local formatters, lower, find, gsub, match = string.formatters, string.lower, string.find, string.gsub, string.match
+local strip = string.strip
+local xmlsprint, xmlcprint, xmltext, xmlcontent, xmlempty = xml.sprint, xml.cprint, xml.text, xml.content, xml.empty
+local lxmlcollected, lxmlfilter = lxml.collected, lxml.filter
+local getid = lxml.getid
+local utfchar, utfcharacters, utfvalues, utfsplit, utflen = utf.char, utf.characters, utf.values, utf.split, utf.len
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+local P, Cs = lpeg.P, lpeg.Cs
+
+local mathml = { }
+moduledata.mathml = mathml
+lxml.mathml = mathml -- for the moment
+
+local context = context
+
+local ctx_enabledelimiter = context.enabledelimiter
+local ctx_disabledelimiter = context.disabledelimiter
+local ctx_xmlflush = context.xmlflush -- better xmlsprint
+
+local ctx_halign = context.halign
+local ctx_noalign = context.noalign
+local ctx_bgroup = context.bgroup
+local ctx_egroup = context.egroup
+local ctx_crcr = context.crcr
+
+local ctx_bTABLE = context.bTABLE
+local ctx_eTABLE = context.eTABLE
+local ctx_bTR = context.bTR
+local ctx_eTR = context.eTR
+local ctx_bTD = context.bTD
+local ctx_eTD = context.eTD
+
+local ctx_mn = context.mn
+local ctx_mi = context.mi
+local ctx_mo = context.mo
+local ctx_startimath = context.startimath
+local ctx_ignorespaces = context.ignorespaces
+local ctx_removeunwantedspaces = context.removeunwantedspaces
+local ctx_stopimath = context.stopimath
+
+local ctx_mmlapplycsymbol = context.mmlapplycsymbol
+
+local ctx_mathopnolimits = context.mathopnolimits
+local ctx_left = context.left
+local ctx_right = context.right
+
+-- an alternative is to remap to private codes, where we can have
+-- different properties .. to be done; this will move and become
+-- generic; we can then make the private ones active in math mode
+
+-- todo: handle opening/closing mo's here ... presentation mml is such a mess ...
+
+characters.registerentities()
+
+local doublebar = utfchar(0x2016)
+
+local n_replacements = {
+-- [" "] = utfchar(0x2002), -- "&textspace;" -> tricky, no &; in mkiv
+ ["."] = "{.}",
+ [","] = "{,}",
+ [" "] = "",
+}
+
+local l_replacements = { -- in main table
+ ["|"] = "\\mmlleftdelimiter\\vert",
+ ["{"] = "\\mmlleftdelimiter\\lbrace",
+ ["("] = "\\mmlleftdelimiter(",
+ ["["] = "\\mmlleftdelimiter[",
+ ["<"] = "\\mmlleftdelimiter<",
+ [doublebar] = "\\mmlleftdelimiter\\Vert",
+}
+local r_replacements = { -- in main table
+ ["|"] = "\\mmlrightdelimiter\\vert",
+ ["}"] = "\\mmlrightdelimiter\\rbrace",
+ [")"] = "\\mmlrightdelimiter)",
+ ["]"] = "\\mmlrightdelimiter]",
+ [">"] = "\\mmlrightdelimiter>",
+ [doublebar] = "\\mmlrightdelimiter\\Vert",
+}
+
+-- todo: play with asciimode and avoid mmlchar
+
+-- we can use the proper names now! todo
+
+local o_replacements = { -- in main table
+ ["@l"] = "\\mmlleftdelimiter.",
+ ["@r"] = "\\mmlrightdelimiter.",
+ ["{"] = "\\mmlleftdelimiter \\lbrace",
+ ["}"] = "\\mmlrightdelimiter\\rbrace",
+ ["|"] = "\\mmlleftorrightdelimiter\\vert",
+ -- ["."] = "\\mmlleftorrightdelimiter.",
+ ["/"] = "\\mmlleftorrightdelimiter\\solidus",
+ [doublebar] = "\\mmlleftorrightdelimiter\\Vert",
+ ["("] = "\\mmlleftdelimiter(",
+ [")"] = "\\mmlrightdelimiter)",
+ ["["] = "\\mmlleftdelimiter[",
+ ["]"] = "\\mmlrightdelimiter]",
+ -- ["<"] = "\\mmlleftdelimiter<",
+ -- [">"] = "\\mmlrightdelimiter>",
+ ["#"] = "\\mmlchar{35}",
+ ["$"] = "\\mmlchar{36}", -- $
+ ["%"] = "\\mmlchar{37}",
+ ["&"] = "\\mmlchar{38}",
+ ["^"] = "\\mmlchar{94}{}", -- strange, sometimes luatex math sees the char instead of \char
+ ["_"] = "\\mmlchar{95}{}", -- so we need the {}
+ ["~"] = "\\mmlchar{126}",
+ [" "] = "",
+ ["°"] = "^\\circ", -- hack
+
+ -- [utfchar(0xF103C)] = "\\mmlleftdelimiter<",
+ [utfchar(0xF1026)] = "\\mmlchar{38}",
+ [utfchar(0x02061)] = "", -- function applicator sometimes shows up in font
+ -- [utfchar(0xF103E)] = "\\mmlleftdelimiter>",
+ -- [utfchar(0x000AF)] = '\\mmlchar{"203E}', -- 0x203E
+}
+
+local simpleoperatorremapper = utf.remapper(o_replacements)
+
+--~ languages.data.labels.functions
+
+local i_replacements = {
+ ["sin"] = "\\sin",
+ ["cos"] = "\\cos",
+ ["abs"] = "\\abs",
+ ["arg"] = "\\arg",
+ ["codomain"] = "\\codomain",
+ ["curl"] = "\\curl",
+ ["determinant"] = "\\det",
+ ["divergence"] = "\\div",
+ ["domain"] = "\\domain",
+ ["gcd"] = "\\gcd",
+ ["grad"] = "\\grad",
+ ["identity"] = "\\id",
+ ["image"] = "\\image",
+ ["lcm"] = "\\lcm",
+ ["lim"] = "\\lim",
+ ["max"] = "\\max",
+ ["median"] = "\\median",
+ ["min"] = "\\min",
+ ["mode"] = "\\mode",
+ ["mod"] = "\\mod",
+ ["polar"] = "\\Polar",
+ ["exp"] = "\\exp",
+ ["ln"] = "\\ln",
+ ["log"] = "\\log",
+ ["sin"] = "\\sin",
+ ["arcsin"] = "\\arcsin",
+ ["sinh"] = "\\sinh",
+ ["arcsinh"] = "\\arcsinh",
+ ["cos"] = "\\cos",
+ ["arccos"] = "\\arccos",
+ ["cosh"] = "\\cosh",
+ ["arccosh"] = "\\arccosh",
+ ["tan"] = "\\tan",
+ ["arctan"] = "\\arctan",
+ ["tanh"] = "\\tanh",
+ ["arctanh"] = "\\arctanh",
+ ["cot"] = "\\cot",
+ ["arccot"] = "\\arccot",
+ ["coth"] = "\\coth",
+ ["arccoth"] = "\\arccoth",
+ ["csc"] = "\\csc",
+ ["arccsc"] = "\\arccsc",
+ ["csch"] = "\\csch",
+ ["arccsch"] = "\\arccsch",
+ ["sec"] = "\\sec",
+ ["arcsec"] = "\\arcsec",
+ ["sech"] = "\\sech",
+ ["arcsech"] = "\\arcsech",
+ [" "] = "",
+
+ ["false"] = "{\\mathrm false}",
+ ["notanumber"] = "{\\mathrm NaN}",
+ ["otherwise"] = "{\\mathrm otherwise}",
+ ["true"] = "{\\mathrm true}",
+ ["declare"] = "{\\mathrm declare}",
+ ["as"] = "{\\mathrm as}",
+}
+
+-- we could use a metatable or when accessing fallback on the
+-- key but at least we now have an overview
+
+local csymbols = {
+ arith1 = {
+ lcm = "lcm",
+ big_lcm = "lcm",
+ gcd = "gcd",
+ big_gcd = "big_gcd",
+ plus = "plus",
+ unary_minus = "minus",
+ minus = "minus",
+ times = "times",
+ divide = "divide",
+ power = "power",
+ abs = "abs",
+ root = "root",
+ sum = "sum",
+ product = "product",
+ },
+ fns = {
+ domain = "domain",
+ range = "codomain",
+ image = "image",
+ identity = "ident",
+ -- left_inverse = "",
+ -- right_inverse = "",
+ inverse = "inverse",
+ left_compose = "compose",
+ lambda = "labmda",
+ },
+ linalg1 = {
+ vectorproduct = "vectorproduct",
+ scalarproduct = "scalarproduct",
+ outerproduct = "outerproduct",
+ transpose = "transpose",
+ determinant = "determinant",
+ vector_selector = "selector",
+ -- matrix_selector = "matrix_selector",
+ },
+ logic1 = {
+ equivalent = "equivalent",
+ ["not"] = "not",
+ ["and"] = "and",
+ -- big_and = "",
+ ["xor"] = "xor",
+ -- big_xor = "",
+ ["or"] = "or",
+ -- big-or = "",
+ implies = "implies",
+ ["true"] = "true",
+ ["false"] = "false",
+ },
+ nums1 = {
+ -- based_integer = "based_integer"
+ rational = "rational",
+ inifinity = "infinity",
+ e = "expenonentiale",
+ i = "imaginaryi",
+ pi = "pi",
+ gamma = "gamma",
+ NaN = "NaN",
+ },
+ relation1 = {
+ eq = "eq",
+ lt = "lt",
+ gt = "gt",
+ neq = "neq",
+ leq = "leq",
+ geq = "geq",
+ approx = "approx",
+ },
+ set1 = {
+ cartesian_product = "cartesianproduct",
+ empty_set = "emptyset",
+ map = "map",
+ size = "card",
+ -- suchthat = "suchthat",
+ set = "set",
+ intersect = "intersect",
+ -- big_intersect = "",
+ union = "union",
+ -- big_union = "",
+ setdiff = "setdiff",
+ subset = "subset",
+ ["in"] = "in",
+ notin = "notin",
+ prsubset = "prsubset",
+ notsubset = "notsubset",
+ notprsubset = "notprsubset",
+ },
+ veccalc1 = {
+ divergence = "divergence",
+ grad = "grad",
+ curl = "curl",
+ laplacian = "laplacian",
+ Laplacian = "laplacian",
+ },
+ calculus1 = {
+ diff = "diff",
+ -- nthdiff = "",
+ partialdiff = "partialdiff",
+ int = "int",
+ -- defint = "defint",
+ },
+ integer1 = {
+ factorof = "factorof",
+ factorial = "factorial",
+ quotient = "quotient",
+ remainder = "rem",
+ },
+ linalg2 = {
+ vector = "vector",
+ matrix = "matrix",
+ matrixrow = "matrixrow",
+ },
+ mathmkeys = {
+ -- equiv = "",
+ -- contentequiv = "",
+ -- contentequiv_strict = "",
+ },
+ rounding1 = {
+ ceiling = "ceiling",
+ floor = "floor",
+ -- trunc = "trunc",
+ -- round = "round",
+ },
+ setname1 = {
+ P = "primes",
+ N = "naturalnumbers",
+ Z = "integers",
+ rationals = "rationals",
+ R = "reals",
+ complexes = "complexes",
+ },
+ complex1 = {
+ -- complex_cartesian = "complex_cartesian", -- ci ?
+ real = "real",
+ imaginary = "imaginary",
+ -- complex_polar = "complex_polar", -- ci ?
+ argument = "arg",
+ conjugate = "conjugate",
+ },
+ interval1 = { -- not an apply
+ -- integer_interval = "integer_interval",
+ interval = "interval",
+ interval_oo = { tag = "interval", closure = "open" },
+ interval_cc = { tag = "interval", closure = "closed" },
+ interval_oc = { tag = "interval", closure = "open-closed" },
+ interval_co = { tag = "interval", closure = "closed-open" },
+ },
+ linalg3 = {
+ -- vector = "vector.column",
+ -- matrixcolumn = "matrixcolumn",
+ -- matrix = "matrix.column",
+ },
+ minmax1 = {
+ min = "min",
+ -- big_min = "",
+ max = "max",
+ -- big_max = "",
+ },
+ piece1 = {
+ piecewise = "piecewise",
+ piece = "piece",
+ otherwise = "otherwise",
+ },
+ error1 = {
+ -- unhandled_symbol = "",
+ -- unexpected_symbol = "",
+ -- unsupported_CD = "",
+ },
+ limit1 = {
+ -- limit = "limit",
+ -- both_sides = "both_sides",
+ -- above = "above",
+ -- below = "below",
+ -- null = "null",
+ tendsto = "tendsto",
+ },
+ list1 = {
+ -- map = "",
+ -- suchthat = "",
+ -- list = "list",
+ },
+ multiset1 = {
+ size = { tag = "card", type = "multiset" },
+ cartesian_product = { tag = "cartesianproduct", type = "multiset" },
+ empty_set = { tag = "emptyset", type = "multiset" },
+ -- multi_set = { tag = "multiset", type = "multiset" },
+ intersect = { tag = "intersect", type = "multiset" },
+ -- big_intersect = "",
+ union = { tag = "union", type = "multiset" },
+ -- big_union = "",
+ setdiff = { tag = "setdiff", type = "multiset" },
+ subset = { tag = "subset", type = "multiset" },
+ ["in"] = { tag = "in", type = "multiset" },
+ notin = { tag = "notin", type = "multiset" },
+ prsubset = { tag = "prsubset", type = "multiset" },
+ notsubset = { tag = "notsubset", type = "multiset" },
+ notprsubset = { tag = "notprsubset", type = "multiset" },
+ },
+ quant1 = {
+ forall = "forall",
+ exists = "exists",
+ },
+ s_dist = {
+ -- mean = "mean.dist",
+ -- sdev = "sdev.dist",
+ -- variance = "variance.dist",
+ -- moment = "moment.dist",
+ },
+ s_data = {
+ mean = "mean",
+ sdev = "sdev",
+ variance = "vriance",
+ mode = "mode",
+ median = "median",
+ moment = "moment",
+ },
+ transc1 = {
+ log = "log",
+ ln = "ln",
+ exp = "exp",
+ sin = "sin",
+ cos = "cos",
+ tan = "tan",
+ sec = "sec",
+ csc = "csc",
+ cot = "cot",
+ sinh = "sinh",
+ cosh = "cosh",
+ tanh = "tanh",
+ sech = "sech",
+ csch = "cscs",
+ coth = "coth",
+ arcsin = "arcsin",
+ arccos = "arccos",
+ arctan = "arctan",
+ arcsec = "arcsec",
+ arcscs = "arccsc",
+ arccot = "arccot",
+ arcsinh = "arcsinh",
+ arccosh = "arccosh",
+ arctanh = "arstanh",
+ arcsech = "arcsech",
+ arccsch = "arccsch",
+ arccoth = "arccoth",
+ },
+}
+
+function xml.functions.remapmmlcsymbol(e)
+ local at = e.at
+ local cd = at.cd
+ if cd then
+ cd = csymbols[cd]
+ if cd then
+ local tx = e.dt[1]
+ if tx and tx ~= "" then
+ local tg = cd[tx]
+ if tg then
+ at.cd = nil
+ at.cdbase = nil
+ e.dt = { }
+ if type(tg) == "table" then
+ for k, v in next, tg do
+ if k == "tag" then
+ e.tg = v
+ else
+ at[k] = v
+ end
+ end
+ else
+ e.tg = tg
+ end
+ end
+ end
+ end
+ end
+end
+
+function xml.functions.remapmmlbind(e)
+ e.tg = "apply"
+end
+
+function xml.functions.remapopenmath(e)
+ local tg = e.tg
+ if tg == "OMOBJ" then
+ e.tg = "math"
+ elseif tg == "OMA" then
+ e.tg = "apply"
+ elseif tg == "OMB" then
+ e.tg = "apply"
+ elseif tg == "OMS" then
+ local at = e.at
+ e.tg = "csymbol"
+ e.dt = { at.name or "unknown" }
+ at.name = nil
+ elseif tg == "OMV" then
+ local at = e.at
+ e.tg = "ci"
+ e.dt = { at.name or "unknown" }
+ at.name = nil
+ elseif tg == "OMI" then
+ e.tg = "ci"
+ end
+ e.rn = "mml"
+end
+
+function mathml.checked_operator(str)
+ context(simpleoperatorremapper(str))
+end
+
+function mathml.stripped(str)
+ context(strip(str))
+end
+
+local p_entity = (P("&") * ((1-P(";"))^0) * P(";"))
+local p_utfchar = lpegpatterns.utf8character
+local p_spacing = lpegpatterns.whitespace^1
+
+local p_mn = Cs((p_entity/"" + p_spacing/utfchar(0x205F) + p_utfchar/n_replacements)^0)
+local p_strip = Cs((p_entity/"" + p_utfchar )^0)
+local p_mi = Cs((p_entity/"" + p_utfchar/i_replacements)^0)
+
+-- function mathml.mn(id,pattern)
+-- -- maybe at some point we need to interpret the number, but
+-- -- currently we assume an upright font
+-- local str = xmlcontent(getid(id)) or ""
+-- local rep = gsub(str,"&.-;","")
+-- local rep = gsub(rep,"(%s+)",utfchar(0x205F)) -- medspace e.g.: twenty one (nbsp is not seen)
+-- local rep = gsub(rep,".",n_replacements)
+-- ctx_mn(rep)
+-- end
+
+function mathml.mn(id,pattern)
+ -- maybe at some point we need to interpret the number, but
+ -- currently we assume an upright font
+ ctx_mn(lpegmatch(p_mn,xmlcontent(getid(id)) or ""))
+end
+
+-- function mathml.mo(id)
+-- local str = xmlcontent(getid(id)) or ""
+-- local rep = gsub(str,"&.-;","") -- todo
+-- context(simpleoperatorremapper(rep) or rep)
+-- end
+
+function mathml.mo(id)
+ local str = lpegmatch(p_strip,xmlcontent(getid(id)) or "")
+ context(simpleoperatorremapper(str) or str)
+end
+
+function mathml.mi(id)
+ -- we need to strip comments etc .. todo when reading in tree
+ local e = getid(id)
+ local str = e.dt
+ if type(str) == "table" then
+ local n = #str
+ if n == 0 then
+ -- nothing to do
+ elseif n == 1 then
+ local first = str[1]
+ if type(first) == "string" then
+ -- local str = gsub(first,"&.-;","") -- bah
+ -- local rep = i_replacements[str]
+ -- if not rep then
+ -- rep = gsub(str,".",i_replacements)
+ -- end
+ local str = lpegmatch(p_strip,first)
+ local rep = i_replacements[str] or lpegmatch(p_mi,str)
+ context(rep)
+ -- ctx_mi(rep)
+ else
+ ctx_xmlflush(id) -- xmlsprint or so
+ end
+ else
+ ctx_xmlflush(id) -- xmlsprint or so
+ end
+ else
+ ctx_xmlflush(id) -- xmlsprint or so
+ end
+end
+
+function mathml.mfenced(id) -- multiple separators
+ id = getid(id)
+ local at = id.at
+ local left = at.open or "("
+ local right = at.close or ")"
+ local separators = at.separators or ","
+ local l = l_replacements[left]
+ local r = r_replacements[right]
+ ctx_enabledelimiter()
+ if l then
+ context(l_replacements[left] or o_replacements[left] or "")
+ else
+ context(o_replacements["@l"])
+ context(left)
+ end
+ ctx_disabledelimiter()
+ local collected = lxmlfilter(id,"/*") -- check the *
+ if collected then
+ local n = #collected
+ if n == 0 then
+ -- skip
+ elseif n == 1 then
+ xmlsprint(collected[1]) -- to be checked
+ else
+ local t = utfsplit(separators,true)
+ for i=1,n do
+ xmlsprint(collected[i]) -- to be checked
+ if i < n then
+ local m = t[i] or t[#t] or ""
+ if m == "|" then
+ m = "\\enabledelimiter\\middle|\\relax\\disabledelimiter"
+ elseif m == doublebar then
+ m = "\\enabledelimiter\\middle|\\relax\\disabledelimiter"
+ elseif m == "{" then
+ m = "\\{"
+ elseif m == "}" then
+ m = "\\}"
+ end
+ context(m)
+ end
+ end
+ end
+ end
+ ctx_enabledelimiter()
+ if r then
+ context(r_replacements[right] or o_replacements[right] or "")
+ else
+ context(right)
+ context(o_replacements["@r"])
+ end
+ ctx_disabledelimiter()
+end
+
+local function flush(e,tag,toggle)
+ if tag == "none" then
+ -- if not toggle then
+ context("{}") -- {} starts a new ^_ set
+ -- end
+ elseif toggle then
+ context("^{")
+ xmlsprint(e.dt)
+ context("}{}") -- {} starts a new ^_ set
+ else
+ context("_{")
+ xmlsprint(e.dt)
+ context("}")
+ end
+ return not toggle
+end
+
+function mathml.mmultiscripts(id)
+ local done, toggle = false, false
+ for e in lxmlcollected(id,"/*") do
+ local tag = e.tg
+ if tag == "mprescripts" then
+ context("{}")
+ done = true
+ elseif done then
+ toggle = flush(e,tag,toggle)
+ end
+ end
+ local done, toggle = false, false
+ for e in lxmlcollected(id,"/*") do
+ local tag = e.tg
+ if tag == "mprescripts" then
+ break
+ elseif done then
+ toggle = flush(e,tag,toggle)
+ else
+ xmlsprint(e)
+ done = true
+ end
+ end
+end
+
+local columnalignments = {
+ left = "flushleft",
+ right = "flushright",
+ center = "middle",
+}
+
+local rowalignments = {
+ top = "high",
+ bottom = "low",
+ center = "lohi",
+ baseline = "top",
+ axis = "lohi",
+}
+
+local frametypes = {
+ none = "off",
+ solid = "on",
+ dashed = "on",
+}
+
+-- crazy element ... should be a proper structure instead of such a mess
+
+function mathml.mcolumn(root)
+ root = getid(root)
+ local matrix, numbers = { }, 0
+ local function collect(m,e)
+ local tag = e.tg
+ if tag == "mi" or tag == "mn" or tag == "mo" or tag == "mtext" then
+ local str = xmltext(e)
+ str = lpegmatch(p_strip,str)
+ for s in utfcharacters(str) do
+ m[#m+1] = { tag, s }
+ end
+ if tag == "mn" then
+ local n = utflen(str)
+ if n > numbers then
+ numbers = n
+ end
+ end
+ elseif tag == "mspace" or tag == "mline" then
+ local str = e.at.spacing or ""
+ for s in utfcharacters(str) do
+ m[#m+1] = { tag, s }
+ end
+ -- elseif tag == "mline" then
+ -- m[#m+1] = { tag, e }
+ end
+ end
+ for e in lxmlcollected(root,"/*") do
+ local m = { }
+ matrix[#matrix+1] = m
+ if e.tg == "mrow" then
+ -- only one level
+ for e in lxmlcollected(e,"/*") do
+ collect(m,e)
+ end
+ else
+ collect(m,e)
+ end
+ end
+ ctx_halign()
+ ctx_bgroup()
+ context([[\hss\startimath\alignmark\stopimath\aligntab\startimath\alignmark\stopimath\cr]])
+ for i=1,#matrix do
+ local m = matrix[i]
+ local mline = true
+ for j=1,#m do
+ if m[j][1] ~= "mline" then
+ mline = false
+ break
+ end
+ end
+ if mline then
+ ctx_noalign([[\obeydepth\nointerlineskip]])
+ end
+ for j=1,#m do
+ local mm = m[j]
+ local tag, chr = mm[1], mm[2]
+ if tag == "mline" then
+ -- This code is under construction ... I need some real motivation
+ -- to deal with this kind of crap.
+--~ local n, p = true, true
+--~ for c=1,#matrix do
+--~ local mc = matrix[c][j]
+--~ if mc then
+--~ mc = mc[2]
+--~ if type(mc) ~= "string" then
+--~ n, p = false, false
+--~ break
+--~ elseif find(mc,"^[%d ]$") then -- rangecheck is faster
+--~ -- digit
+--~ elseif not find(mc,"^[%.%,]$") then -- rangecheck is faster
+--~ -- punctuation
+--~ else
+--~ n = false
+--~ break
+--~ end
+--~ end
+--~ end
+--~ if n then
+--~ chr = "\\mmlmcolumndigitrule"
+--~ elseif p then
+--~ chr = "\\mmlmcolumnpunctuationrule"
+--~ else
+--~ chr = "\\mmlmcolumnsymbolrule" -- should be widest char
+--~ end
+ chr = "\\hrulefill"
+ elseif tag == "mspace" then
+ chr = "\\mmlmcolumndigitspace" -- utfchar(0x2007)
+ end
+ if j == numbers + 1 then
+ context("\\aligntab")
+ end
+ local nchr = n_replacements[chr]
+ context(nchr or chr)
+ end
+ ctx_crcr()
+ end
+ ctx_egroup()
+end
+
+local spacesplitter = lpeg.tsplitat(" ")
+
+function mathml.mtable(root)
+ -- todo: align, rowspacing, columnspacing, rowlines, columnlines
+ root = getid(root)
+ local at = root.at
+ local rowalign = at.rowalign
+ local columnalign = at.columnalign
+ local frame = at.frame
+ local rowaligns = rowalign and lpegmatch(spacesplitter,rowalign)
+ local columnaligns = columnalign and lpegmatch(spacesplitter,columnalign)
+ local frames = frame and lpegmatch(spacesplitter,frame)
+ local framespacing = at.framespacing or "0pt"
+ local framespacing = at.framespacing or "-\\ruledlinewidth" -- make this an option
+
+ ctx_bTABLE { frame = frametypes[frame or "none"] or "off", offset = framespacing, background = "" } -- todo: use xtables and definextable
+ for e in lxmlcollected(root,"/(mml:mtr|mml:mlabeledtr)") do
+ ctx_bTR()
+ local at = e.at
+ local col = 0
+ local rfr = at.frame or (frames and frames [#frames])
+ local rra = at.rowalign or (rowaligns and rowaligns [#rowaligns])
+ local rca = at.columnalign or (columnaligns and columnaligns[#columnaligns])
+ local ignorelabel = e.tg == "mlabeledtr"
+ for e in lxmlcollected(e,"/mml:mtd") do -- nested we can use xml.collected
+ col = col + 1
+ if ignorelabel and col == 1 then
+ -- get rid of label, should happen at the document level
+ else
+ local at = e.at
+ local rowspan = at.rowspan or 1
+ local columnspan = at.columnspan or 1
+ local cra = rowalignments [at.rowalign or (rowaligns and rowaligns [col]) or rra or "center"] or "lohi"
+ local cca = columnalignments[at.columnalign or (columnaligns and columnaligns[col]) or rca or "center"] or "middle"
+ local cfr = frametypes [at.frame or (frames and frames [col]) or rfr or "none" ] or "off"
+ ctx_bTD { align = formatters["{%s,%s}"](cra,cca), frame = cfr, nx = columnspan, ny = rowspan }
+ if xmlempty(e,".") then
+ -- nothing, else hsize max
+ else
+ ctx_startimath()
+ -- ctx_ignorespaces()
+ xmlcprint(e)
+ -- ctx_removeunwantedspaces()
+ ctx_stopimath()
+ end
+ ctx_eTD()
+ end
+ end
+ -- if e.tg == "mlabeledtr" then
+ -- ctx_bTD()
+ -- xmlcprint(xml.first(e,"/!mml:mtd"))
+ -- ctx_eTD()
+ -- end
+ ctx_eTR()
+ end
+ ctx_eTABLE()
+end
+
+function mathml.csymbol(root)
+ root = getid(root)
+ local at = root.at
+ local encoding = at.encoding or ""
+ local hash = url.hashed(lower(at.definitionUrl or ""))
+ local full = hash.original or ""
+ local base = hash.path or ""
+ local text = strip(xmltext(root) or "")
+ ctx_mmlapplycsymbol(full,base,encoding,text)
+end
+
+local p = lpeg.Cs(((1-lpegpatterns.whitespace)^1 / "mml:enclose:%0" + (lpegpatterns.whitespace^1)/",")^1)
+
+function mathml.menclosepattern(root)
+ root = getid(root)
+ local a = root.at.notation
+ if a and a ~= "" then
+ context(lpegmatch(p,a))
+ end
+end
+
+function xml.is_element(e,name)
+ return type(e) == "table" and (not name or e.tg == name)
+end
+
+function mathml.cpolar_a(root)
+ root = getid(root)
+ local dt = root.dt
+ ctx_mathopnolimits("Polar")
+ ctx_left(false,"(")
+ for k=1,#dt do
+ local dk = dt[k]
+ if xml.is_element(dk,"sep") then
+ context(",")
+ else
+ xmlsprint(dk)
+ end
+ end
+ ctx_right(false,")")
+end
+
+-- crap .. maybe in char-def a mathml overload
+
+local mathmleq = {
+ [utfchar(0x00AF)] = utfchar(0x203E),
+}
+
+function mathml.extensible(chr)
+ context(mathmleq[chr] or chr)
+end
diff --git a/tex/context/modules/mkiv/x-mathml.mkiv b/tex/context/modules/mkiv/x-mathml.mkiv
new file mode 100644
index 000000000..ec56aa3df
--- /dev/null
+++ b/tex/context/modules/mkiv/x-mathml.mkiv
@@ -0,0 +1,2605 @@
+%D \module
+%D [ file=x-mathml,
+%D version=2008.05.29,
+%D title=\CONTEXT\ XML Modules,
+%D subtitle=\MATHML,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% \xmlfilter{#1}{/*/name()} -> \xmltag
+
+% This module is under construction and will be cleaned up. We use a funny mix of
+% xml, tex and lua. I could rewrite the lot but it also shows how context evolves.
+%
+% I might end up with a lua-only implementation some day. I must find a good reason
+% to spend time on it. In fact, it might even be more messy.
+%
+% no m:text strip (needs checking, maybe nbsp is mandate
+%
+% todo: more will be moved to lua (less hassle)
+% todo: move left/right to the lua end
+%
+% this implememation looks like a hack ... this is because we deal with all weird cases we
+% ran into, including abuse that was supposed to render ok (even if it didn't in other
+% renderers) .. it was simply expected to work that way.
+
+\writestatus{loading}{ConTeXt XML Macros / MathML Renderer}
+
+\unprotect
+
+\usemodule[x][calcmath]
+%usemodule[x][asciimath]
+
+\startmodule [mathml]
+
+\registerctxluafile{x-mathml}{}
+
+\def\ctxmodulemathml#1{\ctxlua{moduledata.mathml.#1}}
+
+\startxmlsetups xml:mml:define
+ \xmlsetsetup{#1} {(formula|subformula)} {mml:formula}
+ \xmlfilter {#1} {omt:*/function(remapopenmath)}
+ \xmlfilter {#1} {mml:bind/function(remapmmlbind)}
+ \xmlfilter {#1} {mml:csymbol/function(remapmmlcsymbol)}
+ \xmlsetsetup{#1} {mml:*} {mml:*}
+ \xmlsetsetup{#1} {mml:apply/mml:apply/mml:inverse/../..} {mml:apply:inverse}
+ \xmlstrip {#1} {(mml:mi|mml:mo|mml:mn|mml:csymbol)}
+\stopxmlsetups
+
+\xmlregisterns{omt}{openmath}
+\xmlregisterns{mml}{mathml}
+
+\xmlregistersetup{xml:mml:define}
+
+% \unexpanded\def\MMLhack % no longer needed
+% {\let\MMLpar\par
+% \let\par\relax
+% \everyvbox{\let\par\MMLpar}}
+
+\xmlmapvalue {mml:math:mode} {display} {\displaymathematics} % we had this already
+\xmlmapvalue {mml:math:mode} {inline} {\inlinemathematics }
+
+\xmlmapvalue {mml:math:display} {block} {\displaymathematics} % before this showed up
+\xmlmapvalue {mml:math:display} {inline} {\inlinemathematics }
+
+\xmlmapvalue {mml:math:dir} {ltr} {\setfalse\c_math_right_to_left\math_basics_synchronize_direction}
+\xmlmapvalue {mml:math:dir} {rtl} {\settrue \c_math_right_to_left\math_basics_synchronize_direction}
+
+\startxmlsetups mml:math
+ \begingroup
+ \enableautofences
+ \xmlval {mml:math:dir} {\xmlatt{#1}{dir}} {}
+ \xmlval {mml:math:display} {\xmlatt{#1}{display}} {
+ \xmlval {mml:math:mode} {\xmlatt{#1}{mode}} {
+ \automathematics
+ }
+ }
+ {
+ %\math_fences_checked_start
+ %\MMLhack
+ \xmlflush{#1}
+ %\math_fences_checked_stop
+ }
+ \endgroup
+\stopxmlsetups
+
+\startxmlsetups mml:imath
+ \inlinemathematics {
+ \enableautofences
+ %\math_fences_checked_start
+ %\MMLhack
+ \xmlflush{#1}
+ %\math_fences_checked_stop
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:dmath
+ \displaymathematics {
+ \enableautofences
+ %\math_fences_checked_start
+ %\MMLhack
+ \xmlflush{#1}
+ %\math_fences_checked_stop
+ }
+\stopxmlsetups
+
+%D First we define some general formula elements.
+
+\startxmlsetups mml:formula
+ \edef\mmlformulalabel {\xmlatt{#1}{label}\xmlatt{#1}{id}}
+ \edef\mmlformulasublabel{\xmlatt{#1}{sublabel}\xmlatt{#1}{id}}
+ \doifsomething\mmlformulalabel{\placeformula[\mmlformulalabel]{\mmlformulasublabel}}
+ \startformula
+ %\MMLhack
+ \xmlfirst{#1}{/mml:math}
+ \stopformula
+\stopxmlsetups
+
+% old delimiter hacks
+%
+% \setfalse\mmlignoredelimiter
+% \settrue \mmlsomeleftdelimiter
+%
+% \def\MMLleftorright
+% {\ifconditional\mmlsomeleftdelimiter
+% \setfalse\mmlsomeleftdelimiter\expandafter\MMLleft
+% \else
+% \settrue \mmlsomeleftdelimiter\expandafter\MMLright
+% \fi}
+%
+% \ifx\MMLleft \undefined \let\MMLleft \firstofoneargument \fi
+% \ifx\MMLright \undefined \let\MMLright \firstofoneargument \fi
+% \ifx\MMLmiddle\undefined \let\MMLmiddle\firstofoneargument \fi
+%
+% \def\mmlleftdelimiter #1{\ifconditional\mmlignoredelimiter#1\else\normalordelimiter{#1}{\MMLleft #1}\fi}
+% \def\mmlrightdelimiter #1{\ifconditional\mmlignoredelimiter#1\else\normalordelimiter{#1}{\MMLright #1}\fi}
+% \def\mmlmiddledelimiter #1{\ifconditional\mmlignoredelimiter#1\else\normalordelimiter{#1}{\MMLmiddle #1}\fi}
+% \def\mmlleftorrightdelimiter#1{\ifconditional\mmlignoredelimiter#1\else\normalordelimiter{#1}{\MMLleftorright#1}\fi}
+
+% new delimiter hacks (assumes wrapping)
+%
+% \math_fences_checked_start
+% \math_fences_checked_stop
+%
+% \math_fences_checked_left
+% \math_fences_checked_middle
+% \math_fences_checked_right
+% \math_fences_checked_left_or_right
+
+\setfalse\mmlignoredelimiter % alternatively we could turn it on/off inside the start/stop and ignore \left\right\middle otherwise
+
+% \def\mmlleftdelimiter {\ifconditional\mmlignoredelimiter\else\expandafter\math_fences_checked_left \fi}
+% \def\mmlrightdelimiter {\ifconditional\mmlignoredelimiter\else\expandafter\math_fences_checked_right \fi}
+% \def\mmlmiddledelimiter {\ifconditional\mmlignoredelimiter\else\expandafter\math_fences_checked_middle \fi}
+% \def\mmlleftorrightdelimiter{\ifconditional\mmlignoredelimiter\else\expandafter\math_fences_checked_left_or_right\fi}
+
+\let\mmlleftdelimiter \autofenceopen
+\let\mmlmiddledelimiter \autofencemiddle
+\let\mmlrightdelimiter \autofenceclose
+\let\mmlleftorrightdelimiter\autofenceboth
+
+% end of delimiter mess
+
+\def\mmlchar#1{\char#1 } % used in lua code
+
+% \newcount\delimiternesting \appendtoks \delimiternesting\zerocount \to \everymathematics
+
+% \def\mmlleftdelimiter #1{\ifconditional\mmlignoredelimiter#1\else\normalordelimiter{#1}{%
+% \advance\delimiternesting\plusone \MMLleft #1}\fi}
+% \def\mmlrightdelimiter #1{\ifconditional\mmlignoredelimiter#1\else\normalordelimiter{#1}{%
+% \advance\delimiternesting\plusone \MMLright#1}\fi}
+% \def\mmlmiddledelimiter#1{\ifconditional\mmlignoredelimiter#1\else\normalordelimiter{#1}{%
+% \ifcase\delimiternesting\MMLleft\else\MMLmiddle\fi#1}\fi}
+
+%D Remark: from now on this is a module and no longer an xtag
+%D filter. There is an intermediate cleaner module but it has
+%D some namespace limitations. Here we do it the \MKIV\ way.
+
+%D The rendering macros:
+
+\def\MMLrm{\mr}
+
+\def\MMLseparator#1{\removeunwantedspaces{#1}\ignorespaces} % nils space after separator
+\def\MMLseparator#1{,} % todo, for europe we need to block the space
+
+%D Since I only had the draft of MathML 2 and later 3 as example of
+%D rendering, there are probably a lot of omissions and
+%D misinterpretations. At least I learned some bits and
+%D pieces of math rendering.
+%D
+%D The main complications were not so much the math, but to
+%D find the most efficient way to handle elements without
+%D spacing beging messed up. The first implementation was
+%D aimed at getting reasonable output, this second
+%D implementation is already better in terms of handling
+%D nesting, and I will definitely need a third one that has
+%D more efficient and less ugly code.
+%D
+%D The \TEX\ part is not that complicated and once the
+%D preprocessor was okay, the rest way just a lot of keying
+%D and testing. It all comes down to gobbling, redefining,
+%D and not so much to parsing.
+%D
+%D The second implementation expanded the whole math sequence
+%D into an internal \TEX\ representation. This is a rather clean
+%D and fast process. Filtering and testing takes place by
+%D redefining teh internal representation macros.
+%D
+%D The third implementation may look a bit more messy in some
+%D respects. This is because in \TEX\ it's not that trivial to
+%D implement a tree handler. We use a stack for the \type {apply}
+%D element and other sequential content. Occasionally we need to
+%D peek into child elements which involves messy code. This
+%D implementation is closer to the normal \XML\ handling in
+%D \CONTEXT.
+
+%D We start with the parent elements and the option handler.
+
+\unexpanded\def\xmlmathmldirective#1{\dosetvalue{MML#1}}
+
+\xmlinstalldirective{mathml}{xmlmathmldirective}
+
+%def\xmlmathmldirective#1#2#3{[#1][#2][#3]\dosetvalue{MML#1}{#2}{#3}}
+
+%D In the styles, options can be set with:
+
+\unexpanded\def\setupMMLappearance[#1]{\dodoubleargument\getparameters[MML#1]} % no @@ because passed to lua
+
+%D We will apply inner math to all bits and pieces made up by an
+%D \type {apply}.
+
+\def\MMLmathinner
+ {\ifinner
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\mathinner
+ \fi}
+
+%D Auxiliary MathML macros: (to be generalized)
+
+\def\mmlfirst #1{\xmlelement{#1}{1}} % we can move these inline if needed
+\def\mmlsecond #1{\xmlelement{#1}{2}}
+\def\mmlthird #1{\xmlelement{#1}{3}}
+\def\mmlprelast#1{\xmlelement{#1}{-2}}
+\def\mmllast #1{\xmlelement{#1}{-1}}
+
+\unexpanded\def\mmlunexpandedfirst #1{\xmlelement{#1}{1}} % we can move these inline if needed
+\unexpanded\def\mmlunexpandedsecond #1{\xmlelement{#1}{2}}
+\unexpanded\def\mmlunexpandedthird #1{\xmlelement{#1}{3}}
+
+\starttexdefinition doifelsemmlfunction #1
+ \xmldoifelse {#1} {/mml:fn} {
+ \firstoftwoarguments
+ } {
+ \xmldoifelse {#1} {/mml:apply/mml:fn} {
+ \firstoftwoarguments
+ } {
+ \xmldoifelse {#1} {/mml:ci[@type=='fn']} {
+ \firstoftwoarguments
+ } {
+ \secondoftwoarguments
+ }
+ }
+ }
+\stoptexdefinition
+
+%D A couple of lists:
+
+\convertargument
+ mml:times|mml:divide|mml:power|%
+ mml:lt|mml:gt|mml:eq|mml:leq|mml:geq|%
+ mml:in|mml:inverse|%
+ mml:fn|%
+ mml:floor|mml:ceiling|%
+ mml:mean|%
+ mml:selector|%
+ mml:abs|mml:int|mml:limit|mml:sum|mml:product|%
+ mml:outerproduct|mml:innerproduct|mml:scalarproduct%
+\to \MMLcmainresetlist
+
+\convertargument
+ mml:sin|mml:arcsin|mml:sinh|mml:arcsinh|%
+ mml:cos|mml:arccos|mml:cosh|mml:arccosh|%
+ mml:tan|mml:arctan|mml:tanh|mml:arctanh|%
+ mml:cot|mml:arccot|mml:coth|mml:arccoth|%
+ mml:csc|mml:arccsc|mml:csch|mml:arccsch|%
+ mml:sec|mml:arcsec|mml:sech|mml:arcsech|%
+ mml:ln|mml:exp|mml:log|%
+ mml:abs|mml:int|mml:limit|mml:sum|mml:product|%
+ mml:fn%
+\to \MMLcfunctionlist
+
+\convertargument
+ mml:sin|mml:arcsin|mml:sinh|mml:arcsinh|%
+ mml:cos|mml:arccos|mml:cosh|mml:arccosh|%
+ mml:tan|mml:arctan|mml:tanh|mml:arctanh|%
+ mml:cot|mml:arccot|mml:coth|mml:arccoth|%
+ mml:csc|mml:arccsc|mml:csch|mml:arccsch|%
+ mml:sec|mml:arcsec|mml:sech|mml:arcsech|%
+ mml:ln|mml:exp|mml:log|%
+ mml:abs%
+\to \MMLcpurefunctionlist
+
+\convertargument
+ mml:diff|mml:partialdiff|mml:root%
+\to \MMLcconstructlist
+
+%D We use inner and grouping (begin/end and no b/e) else we
+%D get problems with 1/2(1+2) and alike (todo: ask taco).
+%D
+%D The problem with apply is that we need to take care of
+%D several situations, like:
+%D
+%D \starttyping
+%D <apply> <.../> ...
+%D <apply> <fn> ...
+%D <apply> <apply> <ci> ...
+%D <apply> <apply> <fn> <ci> ...
+%D \stoptyping
+%D
+%D Because we translated version 2 of this renderer into
+%D version 3 the following definitions may be sub optimal or
+%D more complex than actually needed.
+
+%D We will more more to lua ...
+
+% simple version
+
+\newcount\@MMLlevel \def\MMLcreset{\@MMLlevel\zerocount}
+
+\let\MMLctempresetlist\empty \def\setMMLcreset{\edef\MMLctempresetlist}
+
+\let\MMLdoL\donothing
+\let\MMLdoR\donothing
+
+\newcount\mmlapplydepth \def\MMLcreset{\mmlapplydepth\zerocount}
+
+% \newtoks \@@postponedMMLactions \setfalse \somepostponedMMLactions
+%
+% \def\postponeMMLactions#1%
+% {\global\settrue\somepostponedMMLactions
+% \global\@@postponedMMLactions\expandafter{\the\@@postponedMMLactions#1}}
+%
+% \def\postponedMMLactions
+% {\global\setfalse\somepostponedMMLactions
+% \@EA\global\@EA\@@postponedMMLactions\@EA\emptytoks
+% \the\@@postponedMMLactions}
+
+\startxmlsetups mml:apply
+ \MMLmathinner {
+ \xmldoif {#1} {/(\MMLcmainresetlist\string|\MMLctempresetlist)} {
+ % \MMLcreset
+ }
+ \edef\mmlapplyopentoken {\xmlatt{#1}{open}}
+ \edef\mmlapplyclosetoken{\xmlatt{#1}{close}}
+ \ifcase\mmlapplydepth \else
+ \ifx\mmlapplyopentoken\empty
+ \def\mmlapplyopentoken {(}
+ \def\mmlapplyclosetoken{)}
+ \fi
+ \fi
+ \advance\mmlapplydepth\plusone
+ \begingroup
+ \ifx\mmlapplyopentoken\empty
+ \let\MMLdoL\donothing
+ \let\MMLdoR\donothing
+ \else
+ \edef\MMLdoL{\noexpand\left \mmlapplyopentoken }
+ \edef\MMLdoR{\noexpand\right\mmlapplyclosetoken}
+ \fi
+ \let\MMLctempresetlist\empty
+ \xmldoifelse {#1} {/mml:apply} {
+% % <apply> <apply> ... </apply> <ci> .. </ci> </apply>
+% \xmldoifelse {#1} {/mml:apply(mml:plus|mml:minus)} {% [a]
+% % yet incomplete and rather untested
+% % <apply> <apply> <minus/> <tan/> <cos/> </apply> <ci>x</ci> </apply>
+ } {% [b]
+% \MMLcreset
+ }
+% \MMLdoL
+% \mmlfirst{#1}
+% \ifconditional\somepostponedMMLactions
+% \postponedMMLactions
+% \else
+% \left(\MMLcreset\mmlsecond{#1}\right)
+% \fi
+% \MMLdoR
+% } {
+ \edef\mmlapplyaction{\xmlfilter{#1}{/*/name()}}
+ \doifelsesetups {mml:apply:mml:\mmlapplyaction} {
+ \xmlsetup{#1}{mml:apply:mml:\mmlapplyaction}
+ } {
+% \MMLdoL
+ \xmlsetup{#1}{mml:\xmlfilter{#1}{/*/name()}}
+% \MMLdoR
+ }
+% }
+ \endgroup
+ \advance\mmlapplydepth\minusone
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:apply:mml:apply
+ \xmlflush{#1}
+ \xmlall{#1}{../[position()>1]}
+\stopxmlsetups
+
+\startxmlsetups mml:apply:mml:fn
+ \xmldoifelse {#1} {/mml:fn/mml:ci} {
+ \edef\mmlfnci{\xmlstripped{#1}{/mml:fn/mml:ci}}% was xmlcontent
+ \doifelsesetups{mmc:fn:\mmlfnci} { % was mmc:fn:...
+ \xmlsetup{#1}{mmc:fn:\mmlfnci} % \MMLdoL/MMLdoR to be handled in plugin
+ } {
+ \MMLcreset
+ \MMLdoL
+ \mmlfirst{#1}
+ \ifnum\xmlcount{#1}{/*}>\plusone
+ \negthinspace % not enough
+ \left(\MMLcreset\xmlconcatrange{#1}{/*}{2}{}{\MMLseparator,}\right)
+ \fi
+ \MMLdoR
+ }
+ } {
+ \MMLcreset
+ \MMLdoL
+ \xmlall{#1}{/*}
+ \MMLdoR
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:apply:mml:csymbol
+ \xmlsetup{#1}{mml:csymbol}% \MMLdoL/MMLdoR to be handled in plugin
+\stopxmlsetups
+
+\startxmlsetups mml:apply:mml:ci
+ \xmlfirst{#1}{/mml:ci}
+ \ifnum\xmlcount{#1}{/*}>\plusone
+ \left(\MMLcreset\xmlconcatrange{#1}{/*}{2}{}{\MMLseparator,}\right)
+ \fi
+\stopxmlsetups
+
+% reln
+
+\startxmlsetups mml:reln
+ \writestatus{XML}{MathML element "reln" is obsolete}
+\stopxmlsetups
+
+% fn
+
+% plusminus ±
+
+\startxmlsetups mmc:fn:\utfchar{"00B1}
+ \MMLdoL
+ \xmlconcat{#1}{/[position()>1]}{\utfchar{"00B1}}
+ \MMLdoR
+\stopxmlsetups
+
+% minusplus
+
+\startxmlsetups mmc:fn:\utfchar{"2213}
+ \MMLdoL
+ \xmlconcat{#1}{/[position()>1]}{\utfchar{"2213}}
+ \MMLdoR
+\stopxmlsetups
+
+\startxmlsetups mmc:fn
+ \begingroup
+ \edef\mmlnoffn{\xmlcount{#1}{/*}}
+ \ifnum\mmlnoffn>\plustwo
+ \def\MMCfnleft {\left(}
+ \def\MMCfnright{\right)}
+ \else
+ \let\MMCfnleft \relax
+ \let\MMCfnright\relax
+ \fi
+ \xmldoifelse {#1} {/mml:ci} { % first
+ \edef\mmlfnci{\xmltext{#1}{/mml:ci}}% was xmlcontent
+ \doifelsesetups{mmc:fn:\mmlfnci} { % was mmc:fn:...
+ \xmlsetup{#1}{mmc:fn:\mmlfnci} % \MMLdoL/MMLdoR to be handled in plugin
+ } {
+ \MMLcreset
+ \mmlfirst{#1}
+ }
+ } {
+ \xmldoifelse {#1} {/mml:apply} { % first
+ \xmldoifelse {#1} {/(mml:plus\string|mml:minus)} {
+ \left(\mmlfirst{#1}\right)
+ } {
+ \mmlfirst{#1}
+ }
+ \ifnum\mmlnoffn>\plusone
+ \left(\xmlall{#1}{/!mml:apply}\right)
+ \fi
+ } {
+ \MMLcreset
+ \negthinspace
+ \MMCfnleft
+ \ifnum\mmlnoffn=\plustwo,\fi
+ \xmlconcat{#1}{/*}{2}{}{\MMLseparator,}
+ \MMCfnright
+ }
+ }
+ \endgroup
+\stopxmlsetups
+
+\startxmlsetups mmc:fn:apply % where used?
+ \xmldoifelse {#1} {/mml:ci} { % first
+ \edef\mmlfnci{\xmltext{#1}{/mml:ci}}% was xmlcontent
+ \doifelsesetups{mmc:fn:\mmlfnci} { % was mmc:fn:...
+ \xmlsetup{#1}{mmc:fn:\mmlfnci} % \MMLdoL/MMLdoR to be handled in plugin
+ } {
+ \MMLcreset
+ \mmlfirst{#1}
+ \ifnum\xmlcount{#1}{/*}>\plusone
+ \negthinspace
+ \left(\MMLcreset\xmlconcat{#1}{2}{}{\MMLseparator,}\right)
+ \fi
+ }
+ } {
+ \endgroup
+ \MMLcreset
+ \mmlfirst{#1}
+ }
+\stopxmlsetups
+
+%D The next definition provide a kind of plug-in mechanism (see
+%D the open math extension module).
+
+% http://www.publishers.com/somename
+%
+% called at the lua end
+
+\starttexdefinition mmlapplycsymbol #1#2#3#4
+ % #1=full url, #2=name, #3=encoding, #4=text
+ \doifelse {#3} {text} {
+ \text{#4}
+ } {
+ \doifelsesetups {mml:csymbol:#1} {
+ % full url
+ \fastsetup{mml:csymbol:#1}
+ } {
+ % somename (fallback)
+ \doifelsesetups {mml:csymbol:#2} {
+ \fastsetup{mml:csymbol:#2}
+ } {
+ \xmlval{mmc:cs}{#3}{}% todo
+ }
+ }
+ }
+\stoptexdefinition
+
+\startxmlsetups mml:csymbol
+ \ctxmodulemathml{csymbol("#1")}
+\stopxmlsetups
+
+\startxmlsetups mml:csymbol:cdots
+ \cdots
+\stopxmlsetups
+
+% \startxmlsetups mml:csymbol:<url> \stopxmlsetups
+
+%D Alternative b will convert periods into comma's:
+
+\setupMMLappearance[cn] [\c!alternative=\v!a]
+\setupMMLappearance[polar] [\c!alternative=\v!a] % a|b|c
+\setupMMLappearance[float] [\c!symbol=\v!no] % \v!yes|dot
+\setupMMLappearance[enotation][\c!symbol=\v!no] % \v!yes|dot
+\setupMMLappearance[base] [\c!symbol=\v!numbers] % digits|characters|text|no
+
+\startxmlsetups mml:cs \xmlcommand{#1}{/}{mml:cs:\xmlattdef{#1}{type}{default}} \stopxmlsetups
+\startxmlsetups mml:ci \xmlcommand{#1}{/}{mml:ci:\xmlattdef{#1}{type}{default}} \stopxmlsetups
+\startxmlsetups mml:cn \xmlcommand{#1}{/}{mml:cn:\xmlattdef{#1}{type}{default}} \stopxmlsetups
+
+% helpers cn / todo: \mn{...}
+
+\startxmlsetups mml:cn:default
+ \mathopnolimits{\xmlflush{#1}}
+\stopxmlsetups
+
+% helpers ci
+
+\startxmlsetups mml:ci:default
+ \xmlflush{#1}
+\stopxmlsetups
+
+\startxmlsetups mml:ci:set
+ {\blackboard{\xmlflush{#1}}} % todo
+\stopxmlsetups
+
+\startxmlsetups mml:ci:vector
+ \overrightarrow{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups mml:ci:matrix
+ {\bi\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups mml:ci:function
+ \xmlflush{#1}% \negthinspace
+\stopxmlsetups
+
+\startxmlsetups mml:ci:fn
+ \xmlsetup{#1}{mml:ci:function}
+\stopxmlsetups
+
+\startxmlsetups mml:ci:complex-cartesian
+ \xmlsetup{#1}{mml:cn:complex}
+\stopxmlsetups
+
+\startxmlsetups mml:ci:complex
+ \xmlsetup{#1}{mml:cn:complex}
+\stopxmlsetups
+
+\startxmlsetups mml:ci:complex-polar
+ \xmlsetup{#1}{mml:cn:polar}
+\stopxmlsetups
+
+\startxmlsetups mml:ci:polar
+ \xmlsetup{#1}{mml:cn:polar}
+\stopxmlsetups
+
+% helpers ci
+
+\startxmlsetups mml:cn:default
+ \xmlflush{#1}
+\stopxmlsetups
+
+\startxmlsetups mml:cn:integer
+ \edef\mmlintegerbase{\xmlattdef{#1}{base}{}}
+ \ifx\mmlintegerbase\empty
+ \xmlflush{#1}
+ \else
+ \doifelse \MMLbasesymbol \v!no {
+ \MMLcCNbasedata{\xmlflush{#1}}
+ } {
+ \MMLcCNbasedata{\xmlflush{#1}}\normalsubscript{
+ \hbox {\startimath
+ \mr
+ \scriptscriptstyle
+ \processaction
+ [\MMLbasesymbol]
+ [\v!characters=>\MMLcCNbasestring BODH,
+ \v!text=>\MMLcCNbasestring{BIN}{OCT}{DEC}{HEX},
+ \s!unknown=>\mmlintegerbase]
+ \stopimath}
+ }
+ }
+ \fi
+\stopxmlsetups
+
+\def\MMLcCNbasedata#1%
+ {\ifnum\mmlintegerbase>10 \relax{\mr#1}\else#1\fi}
+
+\def\MMLcCNbasestring#1#2#3#4%
+ {\ifnum\mmlintegerbase= 2 #1\else
+ \ifnum\mmlintegerbase= 8 #2\else
+ \ifnum\mmlintegerbase=10 #3\else
+ \ifnum\mmlintegerbase=16 #4\else
+ \mmlintegerbase \fi\fi\fi\fi}
+
+\startxmlsetups mml:cn:polar
+ \xmlsetup{#1}{mml:cn:polar:\MMLpolaralternative}
+\stopxmlsetups
+
+\startxmlsetups mml:cn:polar:a
+ \ctxmodulemathml{cpolar_a("#1")}
+\stopxmlsetups
+
+\startxmlsetups mml:cn:polar:b
+ {\mr e}\normalsuperscript{\xmlsnippet{#1}{1}+\xmlsnippet{#1}{3}\thinspace{\mr i}}
+\stopxmlsetups
+
+\startxmlsetups mml:cn:polar:c
+ \exp\left(\xmlsnippet{#1}{1}+\xmlsnippet{#1}{3}\thinspace{\mr i}\right)
+\stopxmlsetups
+
+\startxmlsetups mml:cn:complex-polar
+ \xmlsetup{#1}{mml:cn:polar}
+\stopxmlsetups
+
+\startxmlsetups mml:cn:complex % todo ( )
+ \left(\xmlsnippet{#1}{1} + \xmlsnippet{#1}{3}\thinspace{\mr i}\right)
+\stopxmlsetups
+
+\startxmlsetups mml:cn:complex-cartesian
+ \xmlsetup{#1}{mml:cn:complex}
+\stopxmlsetups
+
+\startxmlsetups mml:cn:float
+ \doifelse \MMLfloatsymbol \v!no {
+ % make sure that e shows up ok
+ \mathopnolimits{\xmlflush{#1}}
+ } {
+ % we should ignore \entities !
+ \edef\mmlfloatstring{\xmlflush{#1}}
+ \splitstring\mmlfloatstring\at e\to\first\and\last
+ \ifx\first\empty
+ \mmlfloatstring
+ \else\ifx\last\empty
+ \mmlfloatstring
+ \else
+ \first
+ \doifelse \MMLfloatsymbol {dot} \cdot \times
+ 10\normalsuperscript{\last}
+ \fi \fi
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:cn:real
+ \xmlsetup{#1}{mml:cn:float}
+\stopxmlsetups
+
+\startxmlsetups mml:cn:e-notation
+ \doifelse \MMLenotationsymbol \v!no {
+ \xmlsnippet{#1}{1}
+ \unskip\mathopnolimits{e}\ignorespaces
+ \xmlsnippet{#1}{3}
+ } {
+ \xmlsnippet{#1}{1}
+ \doifelse \MMLenotationsymbol {dot} \cdot
+ \times10\normalsuperscript{\xmlsnippet{#1}{3}}
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:cn:logical
+ \mathopnolimits{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups mml:cn:rational
+ \xmldoifelse {#1} {/mml:sep} {
+ \mmlfrac
+ {\xmlsnippet{#1}{1}}
+ {\xmlsnippet{#1}{3}}
+ } {
+ \xmlflush{#1}
+ }
+\stopxmlsetups
+
+% interval
+
+\setupMMLappearance[interval][\c!alternative=\v!a,\c!separator={,}]
+
+% when empty element, then it's an apply
+
+\startxmlsetups mml:interval
+ \doifelse {\xmltag{#1}} {apply} {
+ % #1 == apply
+ \let\mmlintervalfirst \mmlsecond
+ \let\mmlintervalsecond\mmlthird
+ \xmlsetup{#1}{mml:interval:\xmlattributedef{#1}{/mml:interval}{closure}{closed}}
+ } {
+ % #1 == interval
+ \let\mmlintervalfirst \mmlfirst
+ \let\mmlintervalsecond\mmlsecond
+ \xmlsetup{#1}{mml:interval:\xmlattdef{#1}{closure}{closed}}
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:interval:closed
+ \left[\mmlintervalfirst{#1}\MMLseparator\MMLintervalseparator\mmlintervalsecond{#1}\right]
+\stopxmlsetups
+
+\startxmlsetups mml:interval:open-closed
+ \doifelse \MMLintervalalternative \v!b {
+ \left<\mmlintervalfirst{#1}\MMLseparator\MMLintervalseparator\mmlintervalsecond{#1}\right]
+ } {
+ \left(\mmlintervalfirst{#1}\MMLseparator\MMLintervalseparator\mmlintervalsecond{#1}\right]
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:interval:closed-open
+ \doifelse \MMLintervalalternative \v!b {
+ \left[\mmlintervalfirst{#1}\MMLseparator\MMLintervalseparator\mmlintervalsecond{#1}\right>
+ } {
+ \left[\mmlintervalfirst{#1}\MMLseparator\MMLintervalseparator\mmlintervalsecond{#1}\right)
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:interval:open
+ \doifelse \MMLintervalalternative \v!b {
+ \left<\mmlintervalfirst{#1}\MMLseparator\MMLintervalseparator\mmlintervalsecond{#1}\right>
+ } {
+ \left(\mmlintervalfirst{#1}\MMLseparator\MMLintervalseparator\mmlintervalsecond{#1}\right)
+ }
+\stopxmlsetups
+
+% inverse
+
+\setfalse\xmlinversefunction
+
+\startxmlsetups mml:apply:inverse
+ \settrue\xmlinversefunction
+ \xmlsetup{#1}{mml:\xmlfilter{#1}{/mml:apply/*[2]/name()}}
+\stopxmlsetups
+
+% condition
+
+% maybe a fast \xmlnonfirst
+
+% instead of the following we could do \xmlcontent{#1}{/mml:bvar} etc
+
+\startxmlsetups mml:bvar \xmlflush{#1} \stopxmlsetups
+\startxmlsetups mml:lowlimit \xmlflush{#1} \stopxmlsetups
+\startxmlsetups mml:uplimit \xmlflush{#1} \stopxmlsetups
+\startxmlsetups mml:degree \xmlflush{#1} \stopxmlsetups
+\startxmlsetups mml:logbase \xmlflush{#1} \stopxmlsetups
+\startxmlsetups mml:fn \xmlflush{#1} \stopxmlsetups
+
+\startxmlsetups mml:condition
+% \xmldoif {#1} {/mml:bvar} {
+% \xmlfirst{#1}{/mml:bvar}\mid
+% }
+ \xmlall{#1}{/!(mml:condition\string|mml:bvar)}
+\stopxmlsetups
+
+% declare
+
+\setupMMLappearance[declare][\c!state=\v!start]
+
+\startxmlsetups mml:declare
+ \doif \MMLdeclarestate \v!start {
+ \mathopnolimits{declare}
+ \mmlfirst{#1}
+ \ifnum\xmlcount{#1}{/*}>\plusone
+ \thickspace
+ \mathopnolimits{as}
+ \thickspace
+ \fi
+ \mmlsecond{#1}
+ }
+\stopxmlsetups
+
+% lambda
+
+\setupMMLappearance[lambda][\c!alternative=b]
+
+\startxmlsetups mml:lambda
+ \begingroup
+ \doifelse \MMLlambdaalternative \v!a {
+ \lambda\left(\xmlconcat{#1}{/!mml:lambda}{\MMLseparator,}\right)
+ } {
+ \ifnum\xmlcount{#1}{/mml:bvar}>\plusone
+ \left(\xmlconcat{#1}{/mml:bvar}{\MMLseparator,}\right)
+ \else
+ \xmlfirst{#1}{/mml:bvar}
+ \fi
+ \mapsto
+ \MMLcreset
+ \xmlall{#1}{/!(mml:bvar|mml:lambda)}
+ }
+ \endgroup
+\stopxmlsetups
+
+% compose
+
+\startxmlsetups mml:compose
+ \begingroup
+ \MMLcreset
+% \let\MMLcCIfunction\firstofoneargument % brrr ? ? ?
+ \doifelsemmlfunction {#1} {
+ \left(\xmlconcat{#1}{/!mml:compose}{\circ}\right)
+ } {
+ \xmlconcat{#1}{/!mml:compose}{\circ}
+ }
+ \endgroup
+\stopxmlsetups
+
+\startxmlsetups mml:image
+ \mathopnolimits{image} \left( {\mr\xmlfilter{#1}{/!mml:image/tag()}} \right)
+\stopxmlsetups
+
+\setupMMLappearance[piece][\c!separator=]
+
+\startxmlsetups mml:piecewise
+ \processaction
+ [\MMLpieceseparator]
+ [ \v!yes=>\def\theMMLpieceseparator{,&},
+ \v!no=>\def\theMMLpieceseparator{&},
+ \s!default=>\def\theMMLpieceseparator{&},
+ \s!unknown=>\def\theMMLpieceseparator{\,\,\hbox{\MMLpieceseparator}\,\,}]
+ \cases{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups mml:piece
+ \mmlfirst{#1}\theMMLpieceseparator\mathematics{\mmlsecond{#1}}\crcr
+\stopxmlsetups
+
+\startxmlsetups mml:otherwise
+% \xmlflush{#1}\MMLcPIECEseparator&{\mr otherwise}\crcr
+ \xmlflush{#1}&{\mr otherwise}\crcr
+\stopxmlsetups
+
+% end of piece
+
+\startxmlsetups mml:quotient
+ \lfloor\mmlsecond{#1}/\mmlthird{#1}\rfloor
+\stopxmlsetups
+
+\startxmlsetups mml:factorial
+ \xmlall{#1}{/!factorial}!
+\stopxmlsetups
+
+\setupMMLappearance [divide] [\c!level=\!!maxcard,\c!alternative=\v!a]
+
+\newcount\mmldividelevel
+
+\startxmlsetups mml:divide
+ \advance\mmldividelevel\plusone
+ \doifelse \MMLdividealternative \v!b {
+ \mmlsecond{#1}/\mmlthird{#1}
+ } {
+ \ifnum \mmldividelevel > \MMLdividelevel \relax % threshold
+ \mmlsecond{#1}/\mmlthird{#1}
+ \else
+ \MMLcreset
+ \mmlfrac{\MMLcreset\mmlsecond{#1}}{\MMLcreset\mmlthird{#1}}
+ \fi
+ }
+ \advance\mmldividelevel\minusone
+\stopxmlsetups
+
+% min max
+
+\startxmlsetups mml:min \mathopnolimits{min} \xmlsetup{#1}{mml:minmax} \stopxmlsetups
+\startxmlsetups mml:max \mathopnolimits{max} \xmlsetup{#1}{mml:minmax} \stopxmlsetups
+
+\startxmlsetups mml:minmax
+ \xmldoif {#1} {/mml:bvar} {
+ {}\normalsubscript{\xmlfirst{#1}{/mml:bvar}}
+ }
+ \left\{
+ \xmlconcat{#1}{/!(mml:bvar\string|mml:max\string|mml:min)}{\MMLseparator,}
+ \right\}
+\stopxmlsetups
+
+% minus plus
+
+\setupMMLappearance [plus] [\c!alternative=\v!a] % b = no sign -> 3 1/4
+\setupMMLappearance [sign] [\c!reduction=\v!yes]
+
+% alternative b -> geen sign
+
+% branch needed, else (a-b) + (c-d) goes wrong
+% reset check in case of (-x) + 37
+% reset check in case of (-x) + 37
+
+\newcount\mmlpluscounter
+
+\startxmlsetups mml:plus
+ \doifelse \MMLsignreduction \v!yes {
+ \MMLdoL
+ \xmlsetup{#1}{mml:plus:reset}
+ \xmlcommand{#1}{/!mml:plus}{mml:plus:body}
+ \MMLdoR
+ } {
+ \ifnum\xmlcount{#1}{/!mml:plus}=\plusone
+ +\xmlfirst{#1}{/!mml:plus}
+ \else
+ \MMLdoL
+ \xmlconcat{#1}{/!mml:plus}{+}
+ \MMLdoR
+ \fi
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:plus:reset
+ \mmlpluscounter\zerocount
+\stopxmlsetups
+
+\startxmlsetups mml:plus:body
+ \advance\mmlpluscounter\plusone
+ \ifnum\mmlpluscounter>\plusone
+ \xmldoifelse{#1}{/mml:minus} {
+ \ifnum\xmlcount{#1}{/!mml:minus}>\plusone
+ +
+ \fi
+ } {
+ \doifelse {\xmlatt{#1}{type}} {rational} {
+ % fraction
+ } {
+ +
+ }
+ }
+ \fi
+ \xmldirect{#1}
+\stopxmlsetups
+
+\newcount\mmlminuscounter
+
+\startsetups mml:minus
+ \doifelse \MMLsignreduction \v!yes {
+ \ifnum\xmlcount{#1}{/!mml:minus}=\plusone
+ -\xmlfirst{#1}{/!mml:minus}
+ \else
+ \MMLdoL
+ \xmlsetup{#1}{mml:minus:reset}
+ \xmlcommand{#1}{/!mml:minus}{mml:minus:body}
+ \MMLdoR
+ \fi
+ } {
+ \left( % \MMLdoL
+ \ifnum\xmlcount{#1}{/!mml:minus}=\plusone
+ -\xmlfirst{#1}{/!mml:minus}
+ \else
+ \xmlsetup{#1}{mml:minus:reset}
+ \xmlcommand{#1}{/!mml:minus}{mml:minus:body}
+ \fi
+ \right) % \MMLdoR
+ }
+\stopsetups
+
+\startxmlsetups mml:minus:reset
+ \mmlminuscounter\zerocount
+\stopxmlsetups
+
+\startxmlsetups mml:minus:body
+ % we can slso use concat here
+ \advance\mmlminuscounter\plusone
+ \ifnum\mmlminuscounter>\plusone
+ -
+ \fi
+ \xmldirect{#1}
+\stopxmlsetups
+
+% power
+
+\setupMMLappearance[power][\c!reduction=\v!yes]
+
+\let\MMLpowerelement\empty
+
+\startxmlsetups mml:power
+ \xmldoifelse {#1} {/mml:apply} {
+ \doifelse \MMLpowerreduction \v!yes {
+ \xmldoifelse {#1} {/mml:apply/(\MMLcfunctionlist)} {
+ \gdef\MMLpowerelement{\mmlthird{#1}}% postpone, no xdef
+ \MMLcreset\mmlsecond{#1}
+ } {
+ \left(\MMLcreset\mmlsecond{#1}\right)\normalsuperscript{\MMLcreset\mmlthird{#1}}
+ }
+ } {
+ \left(\MMLcreset\mmlsecond{#1}\right)\normalsuperscript{\MMLcreset\mmlthird{#1}}
+ }
+ } {
+ \mmlsecond{#1}\normalsuperscript{\MMLcreset\mmlthird{#1}}
+ }
+\stopxmlsetups
+
+% rem
+
+\startxmlsetups mml:rem
+ \xmlconcat{#1}{/!mml:rem}{\mathopnolimits{mod}}
+\stopxmlsetups
+
+\setupMMLappearance [times] [\c!symbol=\v!no,\c!auto=\v!yes] % new, auto catches cn cn cn
+
+\startxmlsetups mml:times
+ \setMMLcreset{\MMLcfunctionlist\string|\MMLcconstructlist}%
+ \doifelse\MMLtimesauto\v!no {
+ \let\MMLtimes@@symbol\MMLtimessymbol
+ } {
+ \xmldoifelse {#1} {/mml:cn[name(1) == 'mml:cn']} {% name(1) is next one
+ \doifelseinset\MMLtimessymbol{\v!yes,\v!no} {
+ \let\MMLtimes@@symbol\v!yes
+ } {
+ \let\MMLtimes@@symbol\MMLtimessymbol
+ }
+ } {
+ \let\MMLtimes@@symbol\MMLtimessymbol
+ }
+ }
+ \doifelse\MMLtimes@@symbol\v!yes {
+ \xmlconcat{#1}{/!mml:times}{\times}
+ } {
+ \doifelse\MMLtimes@@symbol{dot} {
+ \xmlconcat{#1}{/!mml:times}{\cdot}
+ } {
+ \doifelse\MMLtimes@@symbol{times} {
+ \xmlconcat{#1}{/!mml:times}{\times}
+ } {
+ \xmlall{#1}{/!mml:times}
+ }
+ }
+ }
+\stopxmlsetups
+
+\setupMMLappearance[root][\c!symbol=\v!yes]
+
+\startxmlsetups mml:root
+ \xmldoifelse {#1} {/mml:degree} {
+ \root
+ \doifnot\MMLrootsymbol\v!no{\MMLcreset\xmltext{#1}{/mml:degree}}
+ \of
+ } {
+ \sqrt
+ }
+ {\MMLcreset\xmlall{#1}{/!(mml:degree\string|mml:root)}}
+\stopxmlsetups
+
+% gcd
+
+\startxmlsetups mml:gcd
+ \begingroup
+ \gcd\left(\MMLcreset\xmlconcat{#1}{/!mml:gcd}{\MMLseparator,}\right)
+ \endgroup
+\stopxmlsetups
+
+% and or xor implies, not
+
+\startxmlsetups mml:and \xmlconcat{#1}{/!mml:and} {\wedge} \stopxmlsetups
+\startxmlsetups mml:or \xmlconcat{#1}{/!mml:or} {\vee} \stopxmlsetups
+\startxmlsetups mml:xor \xmlconcat{#1}{/!mml:xor} {\mathopnolimits{xor}} \stopxmlsetups
+\startxmlsetups mml:implies \xmlconcat{#1}{/!mml:implies}{\Rightarrow} \stopxmlsetups
+\startxmlsetups mml:not \neg \xmlall {#1}{/!mml:not} \stopxmlsetups
+
+% forall exists
+
+%D We need to shift left below rotated A.
+
+\startxmlsetups mml:forall
+ \forall \negthinspace \xmlsetup{#1}{mml:forallexists}
+\stopxmlsetups
+
+\startxmlsetups mml:exists
+ \exists \xmlsetup{#1}{mml:forallexists}
+\stopxmlsetups
+
+\def\mmlforallexistslist{mml:bvar\string|mml:forall\string|mml:exists\string|mml:condition}
+
+\startxmlsetups mml:forallexists
+ \normalsubscript{\xmlconcat{#1}{/mml:bvar}{\MMLseparator,}}
+ \xmldoifelse {#1} {/mml:condition} {
+ \thickspace
+ \begingroup
+ \xmlfirst{#1}{/mml:condition}
+ \endgroup
+ \ifcase\xmlcount{#1}{/!(\mmlforallexistslist)}\relax
+ % nothing
+ \or
+ % == snelle volgende
+ \left\vert
+ \MMLcreset \medspace \xmlconcat{#1}{/!(\mmlforallexistslist)}{}
+ \right.
+ \else
+ % special case
+ \left\vert
+ \matrix {
+ \xmlconcat{#1}{/!(\mmlforallexistslist)}{\hfill\crcr}
+ }
+ \right.
+ \fi
+ } {
+ :\xmlfirst{#1}{/!(\mmlforallexistslist)}
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:abs
+ \left\vert \MMLcreset\xmlall{#1}{/!mml:abs} \right\vert
+\stopxmlsetups
+
+\startxmlsetups mml:conjugate % watch extra {}
+ {\overline{\MMLcreset\xmlall{#1}{/!mml:conjugate}}}
+\stopxmlsetups
+
+\startxmlsetups mml:arg
+ \mathopnolimits{arg} \left( \MMLcreset\xmlall{#1}{/!mml:arg} \right)
+\stopxmlsetups
+
+\startxmlsetups mml:real
+ \Re \left( \MMLcreset \xmlall{#1}{/!mml:real} \right)
+\stopxmlsetups
+
+\startxmlsetups mml:imaginary
+ \Im \ left( \MMLcreset \xmlall{#1}{/!mml:imaginary} \right)
+\stopxmlsetups
+
+\startxmlsetups mml:lcm
+ \mathopnolimits{lcm} \left( \xmlconcat{#1}{/!mml:lcm}{\MMLseparator,} \right)
+\stopxmlsetups
+
+\startxmlsetups mml:floor
+ \lfloor \xmlall{#1}{/!mml:floor} \rfloor
+\stopxmlsetups
+
+\startxmlsetups mml:ceiling
+ \lceiling \xmlall{#1}{/!mml:ceiling} \rceiling
+\stopxmlsetups
+
+% relations
+
+% apply attr or eq
+
+\setupMMLappearance[relation][\c!align=\v!no]
+
+\xmlmapvalue {mml:relation} {eq} {=}
+\xmlmapvalue {mml:relation} {neq} {\neq}
+\xmlmapvalue {mml:relation} {gt} {>}
+\xmlmapvalue {mml:relation} {lt} {<}
+\xmlmapvalue {mml:relation} {geq} {\geq}
+\xmlmapvalue {mml:relation} {leq} {\leq}
+\xmlmapvalue {mml:relation} {equivalent} {\equiv}
+\xmlmapvalue {mml:relation} {approx} {\approx}
+\xmlmapvalue {mml:relation} {factorof} {\mid}
+
+\startxmlsetups mml:eq \xmlsetup{#1}{mml:relation} \stopxmlsetups
+\startxmlsetups mml:neq \xmlsetup{#1}{mml:relation} \stopxmlsetups
+\startxmlsetups mml:gt \xmlsetup{#1}{mml:relation} \stopxmlsetups
+\startxmlsetups mml:lt \xmlsetup{#1}{mml:relation} \stopxmlsetups
+\startxmlsetups mml:geq \xmlsetup{#1}{mml:relation} \stopxmlsetups
+\startxmlsetups mml:leq \xmlsetup{#1}{mml:relation} \stopxmlsetups
+\startxmlsetups mml:equivalent \xmlsetup{#1}{mml:relation} \stopxmlsetups
+\startxmlsetups mml:approx \xmlsetup{#1}{mml:relation} \stopxmlsetups
+\startxmlsetups mml:factorof \xmlsetup{#1}{mml:relation} \stopxmlsetups
+
+\startxmlsetups mml:relation
+ \edef\mmlapplyaction{\xmlfilter{#1}{/*/name()}}
+ \MMLcreset \xmlsetup{#1}{mml:relation:\xmlattdef{#1}{align}{\MMLrelationalign}}
+\stopxmlsetups
+
+\startxmlsetups mml:relation:default
+ \xmlconcatrange{#1}{/*}{2}{}{\xmlval{mml:relation}{\mmlapplyaction}{[\mmlapplyaction]}}
+\stopxmlsetups
+\startxmlsetups mml:relation:last
+ \eqalign {
+ \xmlconcatrange{#1}{/*}{2}{-2}{&\xmlval{mml:relation}{\mmlapplyaction}{[\mmlapplyaction]}\crcr}
+ \mmlprelast{#1}&\xmlval{mml:relation}{\mmlapplyaction}{[\mmlapplyaction]}{}\mmllast{#1}
+ }
+\stopxmlsetups
+\startxmlsetups mml:relation:first
+ \eqalign {
+ \mmlsecond{#1}\xmlval{mml:relation}{\mmlapplyaction}{[\mmlapplyaction]}{}
+ &\xmlconcatrange{#1}{/*}{3}{}{\crcr\xmlval{mml:relation}{\mmlapplyaction}{[\mmlapplyaction]}{}&}
+ }
+\stopxmlsetups
+\startxmlsetups mml:relation:left
+ \eqalign {
+ \xmlconcatrange{#1}{/*}{2}{}{&\xmlval{mml:relation}{\mmlapplyaction}{[\mmlapplyaction]}\crcr}
+ }
+\stopxmlsetups
+\startxmlsetups mml:relation:right
+ \eqalign {
+ &\xmlconcatrange{#1}{/*}{2}{}{\crcr\xmlval{mml:relation}{\mmlapplyaction}{[\mmlapplyaction]}{}&}
+ }
+\stopxmlsetups
+\startxmlsetups mml:relation:no
+ \xmlsetup{#1}{mml:relation:default}
+\stopxmlsetups
+\startxmlsetups mml:relation:yes
+ \xmlsetup{#1}{mml:relation:left}
+\stopxmlsetups
+
+% personal goody:
+
+\edef\MMLcmainresetlist{\MMLcmainresetlist\string|becomes}
+
+\xmlmapvalue {mml:relation} {mml:becomes} {:=}
+
+\startxmlsetups mml:becomes \xmlsetup{#1}{mml:relation} \stopxmlsetups
+
+% calculus and vector calculus
+
+\startxmlsetups mml:domainofapplication
+ \xmlall{#1}{/!mml:domainofapplication}
+\stopxmlsetups
+
+\setupMMLappearance[int][\c!location=\v!top]
+
+\def\doMMLlimits#1{\doifelsevalue{MML#1\c!location}\v!top\limits\nolimits}
+
+\startxmlsetups mml:int
+ \MMLcreset
+ \xmldoifelse {#1} {/mml:domainofapplication} {
+ \int \doMMLlimits{int}\normalsubscript{\xmlfirst{#1}{/mml:domainofapplication}}\relax
+ } {
+ \xmldoifelse {#1} {/mml:condition} {
+ \int \doMMLlimits{int}\normalsubscript{\xmlfirst{#1}{/mml:condition}}\relax
+ } {
+ \xmldoifelse {#1} {/mml:lowlimit} {
+ \int \doMMLlimits{int}\normalsubscript{\xmlfirst{#1}{/mml:lowlimit}}\normalsuperscript{\xmlfirst{#1}{/mml:uplimit}}
+ } {
+ % funny, why do we have lowlimit/uplimit then
+ \xmldoifelse {#1} {/mml:apply/mml:interval} {
+ \int \doMMLlimits{int}\normalsubscript{\xmlindex{#1}{/mml:apply}{2}}\normalsuperscript{\xmlindex{#1}{/mml:apply}{3}}
+ } {
+ \int
+ }
+ }
+ }
+ }
+ \MMLcreset
+ \xmldoifelse {#1} {/mml:apply} {
+ \doifelsemmlfunction {#1} { % todo test
+ \xmlfirst{#1}{/mml:apply}
+ } {
+ % if there are too many () now, we need to be more clever
+ \left( \xmlfirst{#1}{/mml:apply} \right)
+ }
+ } {
+ \xmlfirst{#1}{/mml:ci}
+ }
+ \xmldoifelse {#1} {/mml:bvar} {
+ \thinspace {\mr d} \xmlfirst{#1}{/mml:bvar}
+ } {
+ % nothing
+ }
+\stopxmlsetups
+
+\setupMMLappearance[diff][\c!location=\v!top,\c!alternative=\v!a]
+
+\startxmlsetups mml:diff
+ \MMLcreset
+ \doifelse \MMLdiffalternative \v!a {
+ \xmldoifelse {#1} {/mml:lambda} {
+ % a special case (mathadore/openmath)
+ \mmlfrac {
+ d
+ \normalsuperscript
+ {\xmlfirst{#1}{/mml:bvar}\xmlfirst{#1}{/mml:cn}}
+ {\xmlfirst{#1}{/mml:lambda}\xmlfirst{#1}{/mml:ci}}
+ } {
+ d
+ {\xmlfirst{#1}{/mml:bvar}\xmlfirst{#1}{/mml:ci}}
+ \normalsuperscript
+ {\xmlfirst{#1}{/mml:bvar}\xmlfirst{#1}{/mml:cn}}
+ }
+ } {
+ \xmldoifelse {#1} {/mml:bvar} {
+ \mmlfrac {
+ {\mr d}{
+ \xmldoifelse {#1} {/mml:degree} {
+ \normalsuperscript{\xmlconcat{#1}{/mml:degree}\empty}
+ } {
+ \xmldoif {#1} {/mml:bvar/mml:degree} {
+ \normalsuperscript{\xmlconcat{#1}{/mml:bvar/mml:degree}+}
+ }
+ }
+ }
+ \doif \MMLdifflocation \v!top {
+ \xmldoifelse {#1} {/mml:ci} {
+ \xmlfirst{#1}{/mml:ci}
+ } {
+ \MMLcreset
+ \ifnum\xmlcount{#1}{/mml:apply/*}>\plustwo % hack
+ \left(\xmlfirst{#1}{/mml:apply}\right)
+ \else
+ \xmlfirst{#1}{/mml:apply}
+ \fi
+ }
+ }
+ } {
+ {\mr d}
+ \xmlfirst{#1}{/mml:bvar/!mml:degree}
+ \xmldoif {#1} {/mml:bvar/mml:degree} {
+ \normalsuperscript{\xmlfirst{#1}{/mml:bvar/mml:degree}}
+ }
+ }
+ \doifnot \MMLdifflocation \v!top {
+ \left(\MMLcreset\xmlfirst{#1}{/(mml:apply\string|mml:ci)}\right)
+ }
+ } {
+ % beware, the second {} is needed for the superscript
+ \xmlconcatrange{#1}{/*}{2}{}{}\normalsuperscript\prime
+ }
+ }
+ } {
+ \MMLcreset
+ \xmlfirst{#1}{/(mml:apply\string|mml:ci)}
+ % there can be problems with nested diff's: \normalsuperscript\normalsuperscript{} error
+ % so we add an empty group here
+ {}\normalsuperscript
+ {
+ \xmldoifelse {#1} {/mml:degree} {
+ \edef\mmldegree{\xmlfirst{#1}{/mml:degree/mml:cn}}
+ \ifx\mmldegree\empty
+ % what to do here
+ \else
+ \dorecurse\mmldegree\prime
+ \fi
+ } {
+ \prime
+ }
+ }
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:partialdiff
+ \xmldoifelse {#1} {/mml:list} {
+ {\mr D}\normalsubscript{
+ \begingroup
+ \setfalse\mmllistdelimiters
+ \xmlall{#1}{/mml:list}
+ \endgroup
+ }
+ \xmlfirst{#1}{/(mml:apply\string|mml:reln\string|mml:ci\string|mml:cn)}
+ } {
+ \xmldoifelse {#1} {/mml:bvar} {
+ \mmlfrac {
+ {\mr d}\normalsuperscript{
+ \xmldoifelse {#1} {/mml:degree} {
+ \xmlconcat{#1}{/mml:degree}\empty
+ } {
+ \xmlconcat{#1}{/mml:bvar/mml:degree}+
+ }
+ }
+ \MMLcreset
+ \xmlfirst{#1}{/(mml:apply\string|mml:reln\string|mml:ci\string|mml:cn)}
+ } {
+ \xmldoif {#1}{/mml:bvar/!mml:degree} {
+ \xmlfirst{#1}{/mml:bvar/!mml:degree} \,
+ }
+ {\mr d}\xmlfirst{#1}{/(mml:apply\string|mml:reln\string|mml:ci\string|mml:cn)}
+ \xmldoif {#1} {/mml:bvar/mml:degree} {
+ \normalsuperscript{\xmlfirst{#1}{/mml:bvar/mml:degree}}
+ }
+ }
+ } {
+ \xmlfirst{#1}{/(mml:apply\string|mml:reln\string|mml:ci\string|mml:cn)}
+ }
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:divergence \mathopnolimits{div} \xmlall{#1}{/!mml:divergence} \stopxmlsetups
+\startxmlsetups mml:grad \mathopnolimits{grad} \xmlall{#1}{/!mml:grad} \stopxmlsetups
+\startxmlsetups mml:curl \mathopnolimits{curl} \xmlall{#1}{/!mml:curl} \stopxmlsetups
+\startxmlsetups mml:laplacian \nabla\normalsuperscript2 \xmlall{#1}{/!mml:laplacian} \stopxmlsetups
+\startxmlsetups mml:ident \mathopnolimits{identity} \xmlall{#1}{/!mml:ident} \stopxmlsetups
+
+\setupMMLappearance[domain] [symbol=]
+\setupMMLappearance[codomain][symbol=]
+
+\startxmlsetups mml:domain
+ \doifelsenothing \MMLdomainsymbol {
+ \mathopnolimits{domain}\MMLcreset\xmlall{#1}{/!mml:domain}
+ } {
+ \MMLdomainsymbol\normalsubscript{\xmlall{#1}{/!mml:domain}}
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:codomain
+ \doifelsenothing \MMLcodomainsymbol {
+ \mathopnolimits{codomain}\MMLcreset\xmlall{#1}{/!mml:codomain}
+ } {
+ \MMLcodomainsymbol\normalsubscript{\xmlall{#1}{/!mml:codomain}}
+ }
+\stopxmlsetups
+
+% theory of sets
+
+\startxmlsetups mml:set
+ \left\{
+ \xmldoifelse {#1} {/mml:condition} {
+ \xmlfirst{#1}{/mml:bvar}\,\middle\vert\,\xmlfirst{#1}{/mml:condition}
+ } {
+ \xmlconcat{#1}{/!mml:set}{\MMLseparator,}
+ }
+ \right\}
+ \relax % needed
+\stopxmlsetups
+
+\settrue\mmllistdelimiters
+
+\startxmlsetups mml:list
+ \begingroup
+ \ifconditional\mmllistdelimiters\left [\fi
+ \begingroup
+ \settrue\mmllistdelimiters
+ \xmlconcat{#1}{/!mml:list}{\MMLseparator,}
+ \endgroup
+ \ifconditional\mmllistdelimiters\right]\fi
+ \endgroup
+\stopxmlsetups
+
+\startxmlsetups mml:union \mmlsecond{#1} \cup \mmlthird{#1} \stopxmlsetups
+\startxmlsetups mml:intersect \mmlsecond{#1} \cap \mmlthird{#1} \stopxmlsetups
+\startxmlsetups mml:in \mmlsecond{#1} \in \mmlthird{#1} \stopxmlsetups
+\startxmlsetups mml:notin \mmlsecond{#1} {\not\in} \mmlthird{#1} \stopxmlsetups
+\startxmlsetups mml:subset \mmlsecond{#1} \subset \mmlthird{#1} \stopxmlsetups
+\startxmlsetups mml:prsubset \mmlsecond{#1} \subseteq \mmlthird{#1} \stopxmlsetups
+\startxmlsetups mml:notsubset \mmlsecond{#1} {\not\subset} \mmlthird{#1} \stopxmlsetups
+\startxmlsetups mml:notprsubset \mmlsecond{#1} {\not\subseteq} \mmlthird{#1} \stopxmlsetups
+\startxmlsetups mml:setdiff \mmlsecond{#1} \setminus \mmlthird{#1} \stopxmlsetups
+
+\startxmlsetups mml:card
+ \left\vert \xmlall{#1}{/!mml:card} \right\vert
+\stopxmlsetups
+
+\startxmlsetups mml:cartesianproduct
+ \xmlconcat{#1}{/!mml:cartesianproduct}{\times}
+\stopxmlsetups
+
+% sequences and series
+
+\setupMMLappearance[sum] [\c!location=\v!top]
+\setupMMLappearance[product][\c!location=\v!top]
+
+\xmlmapvalue {mml:sumprod} {sum} {\sum}
+\xmlmapvalue {mml:sumprod} {product} {\prod}
+
+\startxmlsetups mml:sum \edef\mmlsumprodname{sum} \xmlsetup{#1}{mml:sumprod} \stopxmlsetups
+\startxmlsetups mml:product \edef\mmlsumprodname{product} \xmlsetup{#1}{mml:sumprod} \stopxmlsetups
+
+\def\mmlstackedsubscripts#1%
+ {\vbox
+ {\baselineskip\zeropoint % hack, taco vragen
+ \halign{\startimath\scriptstyle\hss\alignmark\alignmark\hss\stopimath\cr#1\crcr}}}
+
+% unfinished
+
+\startxmlsetups mml:sumprod
+ \begingroup
+ \xmldoifelse {#1} {/(mml:condition\string|mml:bvar\string|mml:lowlimit)} {
+ \def\mmlsumprodlower{
+ \normalsubscript{
+ \xmldoifelse {#1} {/mml:condition} {
+ \mmlstackedsubscripts{\xmlconcat{#1}{/mml:condition}{\crcr}}
+ } {
+ \xmldoif {#1} {/mml:bvar} {
+ \xmlfirst{#1}{/mml:bvar}
+ \xmldoif{#1}{/mml:lowlimit}{=}
+ }
+ \xmlfirst{#1}{/mml:lowlimit}
+ }
+ }
+ }
+ } {
+ \let\mmlsumprodlower\empty
+ }
+ \xmldoifelse {#1} {/mml:uplimit} {
+ \def\mmlsumprodupper{\normalsuperscript{\xmlfirst{#1}{/mml:uplimit}}}
+ } {
+ \let\mmlsumprodupper\empty
+ }
+ \xmldoif {#1} {/mml:interval} { % open math converter gives this
+ \edef\mmlintervalfrom{\xmlindex{#1}{/mml:interval}{1}}
+ \edef\mmlintervalto {\xmlindex{#1}{/mml:interval}{2}}
+ \ifx \mmlintervalfrom \empty \else
+ \def\mmlsumprodlower{\normalsubscript{\xmldoif{#1}{/mml:bvar}{\xmlfirst{#1}{/mml:bvar}{=}}\mmlintervalfrom}}
+ \fi
+ \ifx \mmlintervalto \empty \else
+ \def\mmlsumprodupper{\normalsuperscript{\mmlintervalto}}
+ \fi
+ }
+ \MMLcreset
+ \xmlval{mml:sumprod}{\mmlsumprodname}{}\doMMLlimits\mmlsumprodname\mmlsumprodupper\mmlsumprodlower
+ \MMLcreset
+ \xmldoifelse {#1} {/mml:lambda/mml:apply} {
+ \xmlfirst{#1}{/mml:lambda/mml:apply}% a bit of open math conversion mess
+ } {
+ \xmlfirst{#1}{/(mml:apply\string|mml:lambda\string|mml:ci)}%
+ }
+ \endgroup
+\stopxmlsetups
+
+\setupMMLappearance[limit][\c!location=\v!top]
+
+\startxmlsetups mml:limit
+ \MMLcreset \lim
+ \doMMLlimits {limit}\normalsubscript{
+ \MMLcreset
+ \xmldoifelse {#1} {/mml:condition} {
+ \xmlfirst{#1}{/mml:condition}
+ } {
+ \xmldoif {#1} {/mml:bvar} {
+ \xmlfirst{#1}{/mml:bvar}\rightarrow
+ }
+ \xmlfirst{#1}{/mml:lowlimit}
+ }
+ }
+ \begingroup
+ % a bit of open math conversion mess, lambda needed for openmath, ok?
+ \MMLcreset
+ \xmlfirst{#1}{/mml:lambda/mml:apply}
+ \xmlfirst{#1}{/(mml:apply\string|mml:lambda)}
+ \endgroup
+\stopxmlsetups
+
+% consider a faster index
+
+\startxmlsetups mml:tendsto
+ \MMLcreset \mmlsecond{#1}
+ \xmlval {mml:tendsto:type} {\xmlattdef{#1}{type}{default}} {\rightarrow}
+ \MMLcreset \mmlthird{#1}
+\stopxmlsetups
+
+\xmlmapvalue {mml:tendsto:type} {above} {\downarrow}
+\xmlmapvalue {mml:tendsto:type} {below} {\uparrow}
+\xmlmapvalue {mml:tendsto:type} {default} {\rightarrow}
+
+% elementary classical functions
+
+\setupMMLappearance[log][\c!location=\v!right]
+
+\startxmlsetups mml:exp
+% {\mr e}\normalsuperscript{\xmlfirst{#1}{/mml:apply\string|mml:reln\string|mml:ci\string|mml:cn}}
+ {\mr e}\normalsuperscript{\xmlfirst{#1}{/!mml:exp}}
+\stopxmlsetups
+
+\startxmlsetups mml:log
+ \xmldoifelse {#1} {/mml:logbase} {
+ \doifelse \MMLloglocation \v!left {
+ \mathop {
+ {}\normalsuperscript{\xmlfirst{#1}{/mml:logbase}}\negthinspace\mathopnolimits{log}
+ }
+ } {
+ \mathopnolimits{log}\normalsubscript{\xmlfirst{#1}{/mml:logbase}}
+ }
+% \MMLcreset
+ \xmlfirst{#1}{/(mml:apply\string|mml:reln\string|mml:ci\string|mml:cn)}
+% \xmlsetup{#1}{mml:function} % todo, we start elsewhere
+% \mmlthird{#1}
+ } {
+ \mathopnolimits{log}
+% \MMLcreset
+% \xmlsetup{#1}{mml:function} % todo, we start elsewhere
+ \xmlfirst{#1}{/(mml:apply\string|mml:reln\string|mml:ci\string|mml:cn)}
+% \mmlsecond{#1}
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:ln
+ \mathopnolimits{ln}
+ \xmlsetup{#1}{mml:function}
+\stopxmlsetups
+
+% statistics
+
+\startxmlsetups mml:mean \overline {\mmlsecond{#1}} \stopxmlsetups
+\startxmlsetups mml:sdev \sigma \left(\MMLcreset\mmlsecond{#1}\right) \stopxmlsetups
+\startxmlsetups mml:variance \sigma \left(\MMLcreset\mmlsecond{#1}\right)\normalsuperscript2 \stopxmlsetups
+\startxmlsetups mml:median \mathopnolimits{median}\left(\MMLcreset\mmlsecond{#1}\right) \stopxmlsetups
+\startxmlsetups mml:mode \mathopnolimits{mode} \left(\MMLcreset\mmlsecond{#1}\right) \stopxmlsetups
+
+% moments
+
+\startxmlsetups mml:moment
+ \left\langle
+ \xmlfirst{#1}{/(mml:apply\string|mml:reln\string|mml:ci\string|mml:cn)}\normalsuperscript{\xmlfirst{#1}{/mml:degree}}
+ \right\rangle
+ \xmldoif {#1} {mml:momentabout} {
+ \normalsubscript{\xmlfirst{#1}{mml:momentabout}}
+ }
+\stopxmlsetups
+
+% linear algebra
+
+\setupMMLappearance [vector] [\c!direction=\v!horizontal,\c!separator={,}]
+
+\startxmlsetups mml:vector
+ \begingroup
+ \ifnum\xmlcount{#1}{/*}>\plusone
+ \doifelse\MMLvectordirection\v!horizontal {
+ \left(\xmlconcat{#1}{/*}{\MMLseparator\MMLvectorseparator}\right)
+ } {
+ \MMLcreset\left(\matrix{\xmlconcat{#1}{/*}{\MMLseparator\MMLvectorseparator}}\right)
+ }
+ \else
+ \overrightarrow{\charhtstrut\mmlfirst{#1}}
+ \fi
+ \endgroup
+\stopxmlsetups
+
+\settrue\MMCdelmatrix % ( ) when true
+
+\startxmlsetups mml:matrix
+ \begingroup
+ \MMLcreset
+ \ifconditional\MMCdelmatrix
+ \left(\matrix{\xmlcommand{#1}{/mml:matrixrow}{mml:matrixrow:do}}\right)
+ \else
+ \settrue\MMCdelmatrix
+ \matrix{\xmlcommand{#1}{/mml:matrixrow}{mml:matrixrow:do}}
+ \fi
+ \endgroup
+\stopxmlsetups
+
+\startxmlsetups mml:matrixrow
+ \begingroup
+ \MMLcreset
+ \left(\xmlsetup{#1}{mml:matrixrow:do}\right)
+ \endgroup
+\stopxmlsetups
+
+\startxmlsetups mml:matrixrow:do
+ \xmlconcat{#1}{/*}{&}\crcr
+\stopxmlsetups
+
+\startxmlsetups mml:determinant
+ \begingroup
+ \setfalse\MMCdelmatrix
+ \left|\mmlsecond{#1}\right|
+ \endgroup
+\stopxmlsetups
+
+\startxmlsetups mml:transpose
+ \mmlsecond{#1}\normalsuperscript{\mathopnolimits{T}}
+\stopxmlsetups
+
+\startxmlsetups mml:selector
+ \MMLmathinner{\mmlsecond{#1}\normalsubscript{\MMLcreset\xmlconcatrange{#1}{/*}{3}{}{\MMLseparator,}}}
+\stopxmlsetups
+
+\startxmlsetups mml:vectorproduct \mmlsecond{#1}\times \mmlthird{#1} \stopxmlsetups
+\startxmlsetups mml:scalarproduct \mmlsecond{#1}\cdot \mmlthird{#1} \stopxmlsetups
+\startxmlsetups mml:outerproduct \mmlsecond{#1}\otimes\mmlthird{#1} \stopxmlsetups
+
+% semantic mapping elements
+
+\setupMMLappearance[semantics][\c!state=\v!start]
+
+\startxmlsetups mml:semantics
+ \doifelse\MMLsemanticsstate\v!start {
+ \xmlall{#1}{/mml:annotation}
+ } {
+ \xmlall{#1}{/!mml:annotation}
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:annotation
+ \xmldoifelse {#1} {.[oneof(@encoding,'TeX','tex','application/x-tex','TEX','ConTeXt','context','CONTEXT','ctx')]} {
+ \xmlflushcontext{#1}
+ } {
+ \xmldoifelse {#1} {.[oneof(@encoding,'calcmath','cm')]} {
+ \expanded{\calcmath{\xmlflush{#1}}}
+ } {
+ \xmldoifelse {#1} {.[oneof(@encoding,'asciimath','am')]} {
+ \ifdefined\asciimath
+ \expanded{\asciimath{\xmlflush{#1}}}
+ \else
+ \hbox{\tt no am loaded}
+ \fi
+ } {
+ \xmlall{#1}{../!mml:annotation}
+ }
+ }
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:annotation-xml
+ % maybe diagnostics
+\stopxmlsetups
+
+% misc
+
+\startxmlsetups mml:integers \integers \stopxmlsetups
+\startxmlsetups mml:reals \reals \stopxmlsetups
+\startxmlsetups mml:rationals \rationals \stopxmlsetups
+\startxmlsetups mml:naturalnumbers \naturalnumbers \stopxmlsetups
+\startxmlsetups mml:complexes \complexes \stopxmlsetups
+\startxmlsetups mml:primes \primes \stopxmlsetups
+\startxmlsetups mml:exponentiale \mathopnolimits{e} \stopxmlsetups
+\startxmlsetups mml:imaginaryi \mathopnolimits{i} \stopxmlsetups
+\startxmlsetups mml:notanumber \mathopnolimits{NaN} \stopxmlsetups
+\startxmlsetups mml:true \mathopnolimits{true} \stopxmlsetups
+\startxmlsetups mml:false \mathopnolimits{false} \stopxmlsetups
+\startxmlsetups mml:emptyset \mathopnolimits{Ø} \stopxmlsetups
+\startxmlsetups mml:pi \pi \stopxmlsetups
+\startxmlsetups mml:eulergamma \gamma \stopxmlsetups
+\startxmlsetups mml:infinity \infty \stopxmlsetups
+
+% gonio functions
+
+\setupMMLappearance[function][\c!reduction=\v!yes]
+
+% todo: \mfunction which adapts itself when registered as command
+
+% todo: \def\mmlcfunction#1#2{\mathopnolimits{#2}\xmlsetup{#1}{mml:function}}
+
+\startxmlsetups mml:sin \mathcommand {sin}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:sinh \mathcommand {sinh}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:cos \mathcommand {cos}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:cosh \mathcommand {cosh}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:tan \mathcommand {tan}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:tanh \mathcommand {tanh}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:cot \mathcommand {cot}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:coth \mathcommand {coth}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:csc \mathcommand {csc}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:csch \mathcommand {csch}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:sec \mathcommand {sec}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:sech \mathcommand {sech}\xmlsetup{#1}{mml:function} \stopxmlsetups
+
+\startxmlsetups mml:arcsin \mathcommand {arcsin}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:arcsinh \mathcommand{arcsinh}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:arccos \mathcommand {arccos}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:arccosh \mathcommand{arccosh}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:arctan \mathcommand {arctan}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:arctanh \mathcommand{arctanh}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:arccot \mathcommand {arccot}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:arccoth \mathcommand{arccoth}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:arccsc \mathcommand {arccsc}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:arccsch \mathcommand{arccsch}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:arcsec \mathcommand {arcsec}\xmlsetup{#1}{mml:function} \stopxmlsetups
+\startxmlsetups mml:arcsech \mathcommand{arcsech}\xmlsetup{#1}{mml:function} \stopxmlsetups
+
+\startxmlsetups mml:function
+ \ifx\MMLpowerelement\empty
+ \ifconditional\xmlinversefunction\normalsuperscript{-1}\fi
+ \setfalse\xmlinversefunction
+ \else
+ \normalsuperscript{\ifconditional\xmlinversefunction-\fi\MMLpowerelement}
+ \setfalse\xmlinversefunction
+ \glet\MMLpowerelement\empty
+ \fi
+ \xmlsetup{#1}{mml:function:argument}
+\stopxmlsetups
+
+\startxmlsetups mml:function:argument
+ \doifelse \MMLfunctionreduction \v!yes {
+ \xmldoifelse {#1} {/mml:apply} {
+ \xmldoifelse {#1} {/mml:apply/(\MMLcfunctionlist\string|mml:divide)}
+ \donefalse
+ \donetrue
+ } {
+ \donefalse
+ }
+ } {
+ \donetrue
+ }
+ % beware, we still flush from 2 up
+ \ifdone
+ \left(
+ \MMLcreset
+ \xmlall{#1}{/[position()>1]}% \xmlconcatrange{#1}{/*}{2}{}\empty
+ \right)
+ \else
+ \MMLcreset
+ \xmlall{#1}{/[position()>1]}
+ \fi
+\stopxmlsetups
+
+% PRESENTATION MATHML
+
+% helpers: maybe we can need a setting for the uprights
+
+\xmlmapvalue {mml:s} {normal} {\mathupright} % {\mathtf}
+\xmlmapvalue {mml:s} {double-struck} {\mathblackboard}
+\xmlmapvalue {mml:s} {italic} {\mathit}
+\xmlmapvalue {mml:s} {fraktur} {\mathfraktur}
+\xmlmapvalue {mml:s} {script} {\mathscript}
+\xmlmapvalue {mml:s} {bold} {\mb} % {\mathbf}
+\xmlmapvalue {mml:s} {bold-italic} {\mathbi}
+\xmlmapvalue {mml:s} {bold-fraktur} {\mathfraktur\mathbf}
+\xmlmapvalue {mml:s} {bold-script} {\mathscript\mathbf}
+\xmlmapvalue {mml:s} {sans-serif} {\mathss}
+\xmlmapvalue {mml:s} {bold-sans-serif} {\mathss\mathbf}
+\xmlmapvalue {mml:s} {sans-serif-italic} {\mathss\mathit}
+\xmlmapvalue {mml:s} {sans-serif-bold-italic} {\mathss\mathbi}
+\xmlmapvalue {mml:s} {monospace} {\mathtt}
+
+\xmlmapvalue {mml:l} {-} {\let\mmlfrac\tfrac}
+ \let\mmlfrac\frac
+\xmlmapvalue {mml:l} {+} {\let\mmlfrac\sfrac}
+
+% todo: displaystyle=true/false (or whatever else shows up)
+
+\starttexdefinition setmmlmathstyle #1
+ \xmlval{mml:s}{\xmlatt{#1}{mathvariant}}\empty % was: \mmmr
+\stoptexdefinition
+
+\starttexdefinition setmmlscriptlevel #1
+ \xmlval{mml:l}{\xmlatt{#1}{scriptlevel}}{\let\mmlfrac\frac}
+\stoptexdefinition
+
+\starttexdefinition applymmlmathcolor #1#2
+ \edef\mmlmathcolor{\xmlatt{#1}{mathcolor}}
+ \ifx \mmlmathcolor \empty
+ #2
+ \else
+ \color[\mmlmathcolor]{#2}
+ \fi
+\stoptexdefinition
+
+% todo: textbackgrounds
+
+\starttexdefinition applymmlmathbackground #1#2
+ \edef\mmlmathbackground{\xmlatt{#1}{mathbackground}}
+ \ifx \mmlmathbackground \empty
+ #2
+ \else
+ \backgroundline[\mmlmathbackground]{#2}
+ \fi
+\stoptexdefinition
+
+\newsignal\mmltextsignal % not used
+
+\starttexdefinition applymmlsometext #1#2
+ \applymmlmathbackground {#1} {
+ \applymmlmathcolor {#1} {
+ \setmmlmathstyle {#1}
+ #2
+ }
+ }
+\stoptexdefinition
+
+% probably bugged:
+
+\starttexdefinition doMMLfiller #1
+ \pushmacro\doMMLfiller
+ \let\doMMLfiller\gobbleoneargument
+ \gdef\dodoMMLfiller{% where used
+ \disablefiller
+ \mathematics{#1}
+ }
+ \hbox {
+ \def\normalorfiller##1##2{
+ \gdef\dodoMMLfiller{\enablefiller#1}%
+ \let\normalorfiller\gobbletwoarguments
+ }
+ \mathematics{#1}
+ }
+ \popmacro\doMMLfiller
+\stoptexdefinition
+
+% setups
+
+\startxmlsetups mml:mi % todo: mathsize (unlikely) mathcolor (easy) mathbackground (easy)
+ \begingroup
+ \pushmathstyle
+ \setmmlmathstyle{#1}
+ \setmmlscriptlevel{#1}
+ \ctxmodulemathml{mi("#1")}
+ \popmathstyle
+ \endgroup
+\stopxmlsetups
+
+\startxmlsetups mml:mn
+ \ctxmodulemathml{mn("#1")}% no \hbox, would be ok for . , but spoils rest
+\stopxmlsetups
+
+% <m:mo>-</m:mo><m:mn>2</m:mn> and <m:mn>1</m:mn><m:mo>-</m:mo><m:mn>2</m:mn>
+%
+% spacing between - and 2 is taken care of by tex itself
+
+\startxmlsetups mml:mo
+ \doif {\xmlatt{#1}{maxsize}} {1} {\settrue\mmlignoredelimiter}
+ \doif {\xmlatt{#1}{stretchy}} {false} {\settrue\mmlignoredelimiter}
+ \ctxmodulemathml{mo("#1")}
+ \setfalse\mmlignoredelimiter
+\stopxmlsetups
+
+% \startxmlsetups mml:mfenced % {} around separator is needed for spacing
+% \def\MMLleft {\left }% weird
+% \def\MMLright {\right}
+% \def\MMLmiddle{\middle}
+% \ctxmodulemathml{mfenced("#1")}
+% \stopxmlsetups
+
+\startxmlsetups mml:mfenced % {} around separator is needed for spacing
+ %\math_fences_checked_start
+ \ctxmodulemathml{mfenced("#1")}
+ %\math_fences_checked_stop
+\stopxmlsetups
+
+
+\defineoverlay [mml:enclose:box] [\useMPgraphic{mml:enclose:box}]
+\defineoverlay [mml:enclose:roundedbox] [\useMPgraphic{mml:enclose:roundedbox}]
+\defineoverlay [mml:enclose:circle] [\useMPgraphic{mml:enclose:circle}]
+\defineoverlay [mml:enclose:left] [\useMPgraphic{mml:enclose:left}]
+\defineoverlay [mml:enclose:right] [\useMPgraphic{mml:enclose:right}]
+\defineoverlay [mml:enclose:top] [\useMPgraphic{mml:enclose:top}]
+\defineoverlay [mml:enclose:bottom] [\useMPgraphic{mml:enclose:bottom}]
+\defineoverlay [mml:enclose:updiagonalstrike] [\useMPgraphic{mml:enclose:updiagonalstrike}]
+\defineoverlay [mml:enclose:downdiagonalstrike] [\useMPgraphic{mml:enclose:downdiagonalstrike}]
+\defineoverlay [mml:enclose:horizontalstrike] [\useMPgraphic{mml:enclose:horizontalstrike}]
+\defineoverlay [mml:enclose:verticalstrike] [\useMPgraphic{mml:enclose:verticalstrike}]
+
+\startuseMPgraphic{mml:enclose:box}
+ draw OverlayBox withpen pencircle scaled (ExHeight/10) ;
+\stopuseMPgraphic
+\startuseMPgraphic{mml:enclose:roundedbox}
+ draw OverlayBox cornered .5ExHeight withpen pencircle scaled (ExHeight/10) ;
+\stopuseMPgraphic
+\startuseMPgraphic{mml:enclose:circle}
+ draw fullcircle xysized(bbwidth(OverlayBox),bbheight(OverlayBox)) withpen pencircle scaled (ExHeight/10) ;
+\stopuseMPgraphic
+\startuseMPgraphic{mml:enclose:left}
+ draw leftboundary OverlayBox withpen pencircle scaled (ExHeight/10) ;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+\startuseMPgraphic{mml:enclose:right}
+ draw rightboundary OverlayBox withpen pencircle scaled (ExHeight/10) ;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+\startuseMPgraphic{mml:enclose:top}
+ draw topboundary OverlayBox withpen pencircle scaled (ExHeight/10) ;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+\startuseMPgraphic{mml:enclose:bottom}
+ draw bottomboundary OverlayBox withpen pencircle scaled (ExHeight/10) ;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+\startuseMPgraphic{mml:enclose:updiagonalstrike}
+ path p ; p := OverlayBox enlarged -.25ExHeight ;
+ draw llcorner p -- urcorner p withpen pencircle scaled (ExHeight/10) ;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+\startuseMPgraphic{mml:enclose:downdiagonalstrike}
+ path p ; p := OverlayBox enlarged -.25ExHeight ;
+ draw ulcorner p -- lrcorner p withpen pencircle scaled (ExHeight/10) ;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+\startuseMPgraphic{mml:enclose:horizontalstrike}
+ path p ; p := OverlayBox enlarged -.25ExHeight ;
+ draw .5[llcorner p,ulcorner p] -- .5[lrcorner p,urcorner p] withpen pencircle scaled (ExHeight/10) ;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+\startuseMPgraphic{mml:enclose:verticalstrike}
+ path p ; p := OverlayBox enlarged -.25ExHeight ;
+ draw .5[llcorner p,lrcorner p] -- .5[ulcorner p,urcorner p] withpen pencircle scaled (ExHeight/10) ;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+
+\startxmlsetups mml:menclose
+ \edef\mmlmenclosenotation{\ctxmodulemathml{menclosepattern("#1")}}
+ \ifx\mmlmenclosenotation\empty
+ \xmlflush{#1}
+ \else
+ \doifelse \mmlmenclosenotation {mml:enclose:longdiv} {
+ \overline{\left)\strut\xmlflush{#1}\right.}
+ } {
+ \doifelse \mmlmenclosenotation {mml:enclose:actuarial} {
+ \overline{\left.\strut\xmlflush{#1}\right\vert}
+ } {
+ \doifelse \mmlmenclosenotation {mml:enclose:radical} {
+ \sqrt{\xmlflush{#1}}
+ } {
+ % todo: no framed when longdiv, actuarial or radical ? spec ?
+ \vcenter {
+ \framed
+ [frame=off,strut=no,background={\mmlmenclosenotation}] % offset is kind of undefined
+ {\startimath
+ \expanded{\doifelseinset {mml:enclose:longdiv} {\mmlmenclosenotation}} {
+ \overline{\left)\strut\xmlflush{#1}\right.}
+ } {
+ \expanded{\doifelseinset {mml:enclose:actuarial} {\mmlmenclosenotation}} {
+ \overline{\left.\strut\xmlflush{#1}\right\vert}
+ } {
+ \expanded{\doifelseinset {mml:enclose:radical} {\mmlmenclosenotation}} {
+ \sqrt{\xmlflush{#1}}
+ } {
+ \xmlflush{#1}
+ }
+ }
+ }
+ \stopimath}
+ }
+ }
+ }
+ }
+ \fi
+\stopxmlsetups
+
+\xmlmapvalue {mml:mfrac:linethickness} {thin} {.2pt}
+\xmlmapvalue {mml:mfrac:linethickness} {medium} {.4pt}
+\xmlmapvalue {mml:mfrac:linethickness} {thick} {.8pt}
+\xmlmapvalue {mml:mfrac:linethickness} {0} {0pt}
+
+\startxmlsetups mml:mfrac % dodo: handle linethickness in lua + unit
+ \begingroup
+ \edef\mmlfraclinethickness{\xmlatt{#1}{linethickness}}
+ \ifx\mmlfraclinethickness\empty
+ \doifelse{\xmlatt{#1}{bevelled}}{true} {
+ \left.\mmlfirst{#1}\middle/\mmlsecond{#1}\right.% \thinspace\middle/\thinspace
+ } {
+ \mmlfrac{\mmlfirst{#1}}{\mmlsecond{#1}}
+ }
+ \else
+ \doifelse {\xmlval{mml:mfrac:linethickness}{\mmlfraclinethickness}{}} {} {
+ \scratchdimen\xmlval{mml:mfrac:linethickness}\mmlfraclinethickness{.4pt}
+ } {
+ % probably not yet ok
+ \setdimensionwithunit\scratchdimen\mmlfraclinethickness{pt}
+ }
+ {
+ {\mmlfirst{#1}}
+ \above\scratchdimen
+ {\mmlsecond{#1}}
+ }
+ \fi
+ \endgroup
+\stopxmlsetups
+
+\startxmlsetups mml:ms
+ \hbox {
+ \tf % else encoding problems
+ \edef\mmllquote{\xmlatt{#1}{lquote}}
+ \edef\mmlrquote{\xmlatt{#1}{rquote}}
+ \ifx\mmllquote\empty\symbol[leftquotation]\else\mmllquote\fi
+ \applymmlsometext{#1}{\xmlflush{#1}}
+ \ifx\mmlrquote\empty\symbol[rightquotation]\else\mmlrquote\fi
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:mstyle
+ \begingroup
+ \pushmathstyle
+ \setmmlmathstyle{#1}
+ \setmmlscriptlevel{#1}
+ \xmlflush{#1}
+ \popmathstyle
+ \endgroup
+\stopxmlsetups
+
+\setupMMLappearance[text][\c!alternative=\v!b] % a=normal, b=keep spaces
+
+\startxmlsetups mml:mtext
+ \text {
+ \applymmlsometext{#1}{
+ \doifelse \MMLtextalternative \v!a {
+ %\ctxmodulemathml{stripped(\!!bs\xmlflush{#1}\!!es)}
+ \ignorespaces
+ \xmlflush{#1}
+ \removeunwantedspaces
+ } {
+ \xmlflush{#1}
+ }
+ }
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:merror
+ \hbox{\startimath\displaystyle\xmlflush{#1}\stopimath}
+\stopxmlsetups
+
+\startxmlsetups mml:mphantom
+% \phantom{\ignorespaces{}\xmlflush{#1}\unskip} % watch spacing {} hack
+% \phantom{\mathstyle{\ignorespaces{}\xmlflush{#1}\unskip}}%
+ \phantom{\triggermathstyle\normalmathstyle\ignorespaces\xmlflush{#1}\removeunwantedspaces}
+% \mktriggereffect\v!hidden
+% \ignorespaces{}\xmlflush{#1}\unskip % no attributes in math yet
+% \mktriggereffect\v!normal
+\stopxmlsetups
+
+\startxmlsetups mml:mpadded % todo
+ \xmlflush{#1}
+\stopxmlsetups
+
+% mrow / option: no fenced
+
+\startxmlsetups mml:maction
+ \xmlflush{#1}
+\stopxmlsetups
+
+% \startxmlsetups mml:mrow
+% \begingroup
+% \edef\nofmmlrows{\xmlcount{#1}{/mml:mo}}%
+% \ifnum\nofmmlrows=\plustwo
+% \xmldoifelse {#1} {/mml:mo[position()==1 or position()==\nofmmlrows]} {% we need a {}
+% \def\MMLleft {\left }
+% \def\MMLright {\right}
+% \def\MMLmiddle{\middle}
+% \enabledelimiter
+% \checkdelimiters{\xmlall{#1}{/mml:mo}}
+% \fakeleftdelimiter
+% \xmlflush{#1}
+% \fakerightdelimiter
+% \disabledelimiter
+% } {
+% \xmlflush{#1}
+% }
+% \else
+% \xmlflush{#1}
+% \fi
+% \endgroup
+% \stopxmlsetups
+%
+% fails on { ... so we need
+
+% \startxmlsetups mml:mrow
+% \begingroup
+% \xmldoifelse {#1} {/mml:mo[first() or last()]} {% we need a {}
+% \def\MMLleft {\left }
+% \def\MMLright {\right}
+% \def\MMLmiddle{\middle}
+% \enabledelimiter
+% \checkdelimiters{\xmlall{#1}{/mml:mo}}
+% \fakeleftdelimiter
+% \xmlflush{#1}
+% \fakerightdelimiter
+% \disabledelimiter
+% } {
+% \xmlflush{#1}
+% }
+% \endgroup
+% \stopxmlsetups
+%
+% more modern:
+
+\startxmlsetups mml:mrow
+ \begingroup
+ %\xmldoifelse {#1} {/mml:mo[first() or last()]} {% we need a {}
+ % % \math_fences_checked_start
+ % \xmlflush{#1}
+ % \math_fences_checked_stop
+ %} {
+ \xmlflush{#1}
+ %}
+ \endgroup
+\stopxmlsetups
+
+\startxmlsetups mml:msqrt
+ \sqrt{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups mml:mroot
+ \root{\mmlsecond{#1}}\of{\mmlfirst{#1}}
+\stopxmlsetups
+
+\setupMMLappearance[scripts][\c!alternative=\v!a] % {} rond base
+
+% brrr no { } when limop .. todo: better in lua
+% speed up with ifx and setups or just in lua
+
+\let\mmlnucleus\relax
+
+\startxmlsetups mml:msub
+ \edef\mmlnucleus{\xmlraw{#1}{/mml:*[1]}}
+ \doifelse {\utfmathclass\mmlnucleus} {limop} {
+ \mmlfirst{#1} \normalsubscript{\mmlsecond{#1}}
+ } {
+ \doifelse\MMLscriptsalternative\v!a {
+ {\mmlfirst{#1}}\normalsubscript{\mmlsecond{#1}}
+ } {
+ \mmlfirst{#1} \normalsubscript{\mmlsecond{#1}}
+ }
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:msup
+ \edef\mmlnucleus{\xmlraw{#1}{/mml:*[1]}}
+ \doifelse {\utfmathclass\mmlnucleus} {limop} {
+ \mmlfirst{#1} \normalsuperscript{\mmlsecond{#1}}
+ } {
+ \doifelse\MMLscriptsalternative\v!a {
+ {\mmlfirst{#1}}\normalsuperscript{\mmlsecond{#1}}
+ } {
+ \mmlfirst{#1} \normalsuperscript{\mmlsecond{#1}}
+ }
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:msubsup
+ \edef\mmlnucleus{\xmlraw{#1}{/mml:*[1]}}
+ \doifelse {\utfmathclass\mmlnucleus} {limop} {
+ \mmlfirst{#1}\normalsubscript{\mmlsecond{#1}}\normalsuperscript{\mmlthird{#1}}
+ } {
+ \doifelse\MMLscriptsalternative\v!a {
+ {\mmlfirst{#1}}\normalsubscript{\mmlsecond{#1}}\normalsuperscript{\mmlthird{#1}}
+ } {
+ \mmlfirst{#1}\normalsubscript{\mmlsecond{#1}}\normalsuperscript{\mmlthird{#1}}
+ }
+ }
+\stopxmlsetups
+
+% helpers
+
+\unexpanded\def\mmlexecuteifdefined#1%
+ {\ifx#1\empty
+ \expandafter\secondoftwoarguments
+ \else\ifcsname#1\endcsname
+ \doubleexpandafter\firstoftwoarguments
+ \else
+ \doubleexpandafter\secondoftwoarguments
+ \fi\fi
+ {\csname#1\endcsname}}
+
+\def\mmlextensible#1{\ctxmodulemathml{extensible(\!!bs#1\!!es)}}
+
+\definemathtriplet [\v!mathematics] [mmlovertriplet] % or will we use a special instance
+\definemathtriplet [\v!mathematics] [mmlundertriplet] % or will we use a special instance
+\definemathtriplet [\v!mathematics] [mmldoubletriplet] % or will we use a special instance
+
+% common to munder/mover/munderover : will become core helper (speed up too)
+
+\starttexdefinition unexpanded mmlfencedfirst #1
+ %\math_fences_checked_start
+ \mmlunexpandedfirst{#1}
+ %\math_fences_checked_stop
+\stoptexdefinition
+\starttexdefinition unexpanded mmlfencedsecond #1
+ %\math_fences_checked_start
+ \mmlunexpandedsecond{#1}
+ %\math_fences_checked_stop
+\stoptexdefinition
+\starttexdefinition unexpanded mmlfencedthird #1
+ %\math_fences_checked_start
+ \mmlunexpandedthird{#1}
+ %\math_fences_checked_stop
+\stoptexdefinition
+
+% mover
+
+\starttexdefinition unexpanded mmloverabove #1
+ \edef\mmlovercommand{\utfmathfiller\mmlovertoken}
+ \mmlexecuteifdefined\mmlovercommand {\mmlfencedsecond{#1}} \relax
+\stoptexdefinition
+\starttexdefinition unexpanded mmloverbase #1
+ \edef\mmlbasecommand{\utfmathfiller\mmlbasetoken}
+ \mmlexecuteifdefined\mmlbasecommand {\mmlfencedfirst{#1}}
+ \relax
+\stoptexdefinition
+\starttexdefinition unexpanded mmloverbasefiller #1
+ \edef\mmlbasecommand{e\utfmathcommandfiller\mmlbasetoken}
+ \mmlexecuteifdefined\mmlbasecommand \relax {\mmlfencedsecond{#1}} {}
+\stoptexdefinition
+\starttexdefinition unexpanded mmloveraccent #1
+ \edef\mmlovercommand{\utfmathcommandabove\mmlovertoken}
+ \mmlexecuteifdefined\mmlovercommand \relax {\mmlfencedfirst{#1}}
+\stoptexdefinition
+\starttexdefinition unexpanded mmlovertext #1
+ \mmlovertriplet {\mmloverbase{#1}} {\mmloverabove{#1}} {}
+\stoptexdefinition
+\starttexdefinition unexpanded mmloveraccentchecker #1
+ \edef\mmlovertoken{\mmlextensible{\xmlraw{#1}{/mml:*[2]}}}% /text()
+ \doifelseutfmathabove\mmlovertoken \mmloveraccent \mmlovertext {#1}
+\stoptexdefinition
+
+\startxmlsetups mml:mover
+ \edef\mmlbasetoken{\mmlextensible{\xmlraw{#1}{/mml:*[1]}}}% /text()
+ \doifelseutfmathfiller\mmlbasetoken \mmloverbasefiller \mmloveraccentchecker {#1}
+\stopxmlsetups
+
+% munder
+
+\starttexdefinition unexpanded mmlunderbelow #1
+ \edef\mmlundercommand{\utfmathfiller\mmlundertoken}
+ \mmlexecuteifdefined\mmlundercommand {\mmlfencedsecond{#1}} \relax
+\stoptexdefinition
+\starttexdefinition unexpanded mmlunderbase #1
+ \edef\mmlbasecommand{\utfmathfiller\mmlbasetoken}
+ \mmlexecuteifdefined\mmlbasecommand {\mmlfencedfirst{#1}}
+ \relax
+\stoptexdefinition
+\starttexdefinition unexpanded mmlunderbasefiller #1
+ \edef\mmlbasecommand{e\utfmathcommandfiller\mmlbasetoken}%
+ \mmlexecuteifdefined\mmlbasecommand \relax {} {\mmlfencedsecond{#1}}
+\stoptexdefinition
+\starttexdefinition unexpanded mmlunderaccent #1
+ \edef\mmlundercommand{\utfmathcommandbelow\mmlundertoken}
+ \mmlexecuteifdefined\mmlundercommand \relax {\mmlfencedfirst{#1}}
+\stoptexdefinition
+\starttexdefinition unexpanded mmlundertext #1
+ \mmlundertriplet {\mmlunderbase{#1}} {} {\mmlunderbelow{#1}}
+\stoptexdefinition
+\starttexdefinition unexpanded mmlunderaccentchecker #1
+ \edef\mmlundertoken{\mmlextensible{\xmlraw{#1}{/mml:*[2]}}}% /text()
+ \doifelseutfmathbelow\mmlundertoken \mmlunderaccent \mmlundertext {#1}
+\stoptexdefinition
+
+\startxmlsetups mml:munder
+ \edef\mmlbasetoken{\mmlextensible{\xmlraw{#1}{/mml:*[1]}}}% /text()
+ \doifelseutfmathfiller\mmlbasetoken \mmlunderbasefiller \mmlunderaccentchecker {#1}
+\stopxmlsetups
+
+% munderover
+
+\starttexdefinition unexpanded mmlunderoveraccentcheckerUO #1
+ \edef\mmlundercommand{\utfmathcommandbelow\mmlundertoken}
+ \edef\mmlovercommand {\utfmathcommandabove\mmlovertoken}
+ \edef\mmlbasecommand {\mmlovercommand\mmlundercommand}
+ \ifcsname\mmlbasecommand\endcsname
+ \lastnamedcs {\mmlfencedfirst{#1}}
+ \else\ifcsname\mmlundercommand\endcsname
+ \ifcsname\mmlovercommand\endcsname
+ \lastnamedcs {\csname\mmlundercommand\endcsname{\mmlfencedfirst{#1}}}
+ \else
+ \mmldoubletriplet {\csname\mmlundercommand\endcsname{\mmlfencedfirst{#1}}} {\mmlfencedthird{#1}\mmlfencedthird{#1}} {}
+ \fi
+ \else\ifcsname\mmlovercommand\endcsname
+ \mmldoubletriplet {\csname\mmlovercommand\endcsname{\mmlfencedfirst{#1}}} {} {\mmlfencedsecond{#1}}
+ \else
+ \mmlunderoveraccentcheckerTT {#1}
+ \fi\fi\fi
+\stoptexdefinition
+\starttexdefinition unexpanded mmlunderoveraccentcheckerUT #1
+ \edef\mmlundercommand{\utfmathcommandbelow\mmlundertoken}
+ \edef\mmlbasecommand {\mmlundercommand text}
+ \ifcsname\mmlbasecommand\endcsname
+ \lastnamedcs {\mmlfencedfirst{#1}} {\mmlfencedthird{#1}}
+ \else\ifcsname\mmlundercommand\endcsname
+ \mmldoubletriplet {\csname\mmlundercommand\endcsname{\mmlfencedfirst{#1}}} {\mmlfencedthird{#1}} {}
+ \else
+ \mmlunderoveraccentcheckerTT {#1}
+ \fi\fi
+\stoptexdefinition
+\starttexdefinition unexpanded mmlunderoveraccentcheckerOT #1
+ \edef\mmlovercommand{\utfmathcommandabove\mmlovertoken}
+ \edef\mmlbasecommand{\mmlovercommand text}
+ \ifcsname\mmlbasecommand\endcsname
+ \lastnamedcs {\mmlfencedfirst{#1}} {\mmlfencedsecond{#1}}
+ \else\ifcsname\mmlovercommand\endcsname
+ \mmldoubletriplet {\csname\mmlovercommand\endcsname{\mmlfencedfirst{#1}}} {} {\mmlfencedsecond{#1}}
+ \else
+ \mmlunderoveraccentcheckerTT {#1}
+ \fi\fi
+\stoptexdefinition
+\starttexdefinition unexpanded mmlunderoveraccentcheckerTT #1
+ \mmldoubletriplet {\mmlfencedfirst{#1}} {\mmlfencedthird{#1}} {\mmlfencedsecond{#1}} \relax
+\stoptexdefinition
+\starttexdefinition unexpanded mmlunderoveraccentchecker #1
+ \edef\mmlundertoken{\mmlextensible{\xmlraw{#1}{/mml:*[2]}}}% /text()
+ \edef\mmlovertoken {\mmlextensible{\xmlraw{#1}{/mml:*[3]}}}% /text()
+ \doifelseutfmathbelow\mmlundertoken {
+ \doifelseutfmathabove\mmlovertoken \mmlunderoveraccentcheckerUO \mmlunderoveraccentcheckerUT {#1}
+ } {
+ \doifelseutfmathabove\mmlovertoken \mmlunderoveraccentcheckerOT \mmlunderoveraccentcheckerTT {#1}
+ }
+\stoptexdefinition
+\starttexdefinition unexpanded mmlunderoverbasefiller #1
+ \edef\mmlbasecommand{e\utfmathcommandfiller\mmlbasetoken}%
+ \mmlexecuteifdefined\mmlbasecommand \relax {\mmlfencedthird{#1}} {\mmlfencedsecond{#1}}
+\stoptexdefinition
+\startxmlsetups mml:munderover
+ \edef\mmlbasetoken{\mmlextensible{\xmlraw{#1}{/mml:*[1]}}}% /text()
+ \doifelseutfmathfiller\mmlbasetoken \mmlunderoverbasefiller \mmlunderoveraccentchecker {#1}
+\stopxmlsetups
+
+% tables (mml:mtable, mml:mtr, mml:mlabledtr, mml:mtd)
+
+\startxmlsetups mml:mtable % some more attributes need to be supported
+ \vcenter {
+ \hbox {% needed because otherwise positions makr the vcenter wide
+ \ctxmodulemathml{mtable("#1")}
+ }
+ }
+\stopxmlsetups
+
+\startxmlsetups mml:mcolumn
+ \ctxmodulemathml{mcolumn("#1")}
+\stopxmlsetups
+
+\def\mmlsetfakewidth#1{\setbox\scratchbox\hbox{#1}\scratchdimen\wd\scratchbox}
+
+\def\mmlmcolumndigitspace {\mmlsetfakewidth {0}\kern\scratchdimen}
+\def\mmlmcolumndigitrule {\mmlsetfakewidth {0}\vrule \s!width \scratchdimen \s!height .2\points \s!depth .2\points\relax}
+\def\mmlmcolumnsymbolrule {\mmlsetfakewidth{\times}\vrule \s!width \scratchdimen \s!height .2\points \s!depth .2\points\relax}
+\def\mmlmcolumnpunctuationrule{\mmlsetfakewidth {.}\vrule \s!width \scratchdimen \s!height .2\points \s!depth .2\points\relax}
+
+\setupMMLappearance[mspace][\c!option=] % \v!test
+
+\startxmlsetups mml:mspace
+ \begingroup
+ \edef\mmlspacetext{\xmlatt{#1}{spacing}}
+ \ifx\mmlspacetext\empty
+ \scratchwidth \xmlattdef{#1}{width} \!!zeropoint % must be string
+ \scratchheight\xmlattdef{#1}{height}\!!zeropoint
+ \scratchdepth \xmlattdef{#1}{depth} \!!zeropoint
+ \ifdim\scratchheight=\zeropoint
+ \ifdim\scratchdepth=\zeropoint\else
+ \hbox{\vrule\s!depth\scratchdepth\s!height\zeropoint\s!width\zeropoint}%
+ \fi
+ \else
+ \hbox{\vrule\s!depth\zeropoint\s!height\scratchheight\s!width\zeropoint}%
+ \fi
+ \ifdim\scratchwidth=\zeropoint\else
+ \ifx\MMLmspaceoption\v!test
+ \hbox to \scratchwidth{\showstruts\strut\hss\lower2\exheight\hbox{\infofont\xmlattdef{#1}{width}}\hss\strut}
+ \else
+ \hskip\scratchwidth
+ \fi
+ \fi
+ \else
+ \ifx\MMLmspaceoption\v!test
+ \hbox{\showstruts\strut\phantom{\triggermathstyle\normalmathstyle\mmlspacetext}\strut}
+ \else
+ \phantom{\triggermathstyle\normalmathstyle\mmlspacetext}
+ \fi
+ \fi
+ \endgroup
+\stopxmlsetups
+
+% later we can do a better job by manipulating node lists
+
+% \startxmlsetups mml:mline
+% % new, rather undefined, we need to capture a few keywords
+% \edef\mmllinewidth {\xmlatt{#1}{linethickness}}
+% \edef\mmllinetext {\xmlatt{#1}{spacing}}
+% \edef\mmllinelength{\xmlattdef{#1}{length}\!!zeropoint}
+% \ifx\mmllinewidth\empty
+% \!!deptha.5\linewidth
+% \else
+% \!!deptha.5\dimexpr\mmllinewidth\relax
+% \fi
+% \!!heighta\!!deptha
+% \ifx\mmllinetext\empty
+% \ifx\mmllinelength\empty
+% \!!widtha\zeropoint
+% \else
+% \!!widtha\mmllinelength
+% \fi
+% \else
+% \setbox\scratchbox\hbox{\mathematics{\mathstyle{\mmllinetext}}}% not ok
+% \!!widtha\wd\scratchbox
+% \fi
+% \hbox{\vrule\s!width\!!widtha\s!depth\!!deptha\s!height\!!heighta}
+% \stopxmlsetups
+
+\startxmlsetups mml:mglyph % probably never ok (hbox is needed in order to switch to normal font)
+ \begingroup
+ \edef\mmlglyphfontfamily{\xmlatt {#1}{fontfamily}}
+ \edef\mmlglyphalt {\xmlattdef{#1}{alt}{unknown}}
+ \edef\mmlglyphindex {\xmlatt {#1}{index}}
+ \ifx \mmlglyphfontfamily \empty
+ \hbox{\tttf[no fontfamily specified for \mmlglyphalt]}
+ \else\ifx\mmlglyphindex\empty
+ \hbox{\tttf[no index specified for \mmlglyphalt]}
+ \else
+ \hbox{\getglyph\mmlglyphfontfamily\mmlglyphindex}
+ \fi\fi
+ \endgroup
+\stopxmlsetups
+
+\startxmlsetups mml:maligngroup \stopxmlsetups % will be done when needed
+\startxmlsetups mml:malignmark \stopxmlsetups % will be done when needed
+
+\startxmlsetups mml:none \stopxmlsetups
+\startxmlsetups mml:mprescripts \stopxmlsetups
+
+\startxmlsetups mml:mmultiscripts
+ \ctxmodulemathml{mmultiscripts("#1")}
+\stopxmlsetups
+
+% goodie
+
+\definebuffer[mml]
+
+\def\stopmml{\xmlprocessbuffer{@mml@}{\thedefinedbuffer{mml}}{}}
+
+\stopmodule
+
+\protect \endinput
+
+% TODO:
+%
+% <apply><divide/>
+% <apply><minus/>
+% <apply><minus/><ci>b</ci></apply>
+% <apply><minus/><ci>b</ci></apply>
+% <apply><root/> <ci>a</ci></apply>
+% </apply>
+% <apply><minus/>
+% <apply><minus/><ci>b</ci><ci>b</ci></apply>
+% <apply><minus/><ci>b</ci></apply>
+% <apply><root/> <ci>a</ci></apply>
+% </apply>
+% </apply>
+
+% \startmoduletestsection
+%
+% \def\xflushXMLstackwith#1#2#3#4% num bgroup egroup whatever
+% {\dostepwiserecurse{#1}\XMLstacklevel\plusone
+% {#2\relax
+% \ifnum\recurselevel>#1\relax#4\fi
+% \getXMLstackdata\recurselevel
+% #3}}
+%
+% \def\xflushXMLstackfrom#1#2#3%
+% {\dostepwiserecurse{#1}\XMLstacklevel\plusone
+% {#2\getXMLstackdata\recurselevel#3}}
+%
+% \startxmlsetups mml:minus
+% \doif \MMLsignreduction \v!yes {
+% \setMMLcreset{fn,\MMLcfunctionlist}
+% }
+% \ifcase\XMLstacklevel
+% \or
+% % self
+% \or
+% -\getXMLstackdata\plustwo
+% \else
+% \dostepwiserecurse \plustwo \XMLstacklevel \plusone {
+% \begingroup
+% \doifelse {\getXMLstackname\recurselevel} {apply} {
+% \ifnum\recurselevel=\plustwo
+% \begingroup
+% \dodoifelseMMCfunctioninapply \recurselevel {minus} {
+% \ifnum\XMLstacklevel>\plustwo
+% \endgroup
+% \else
+% \endgroup
+% \MMLcreset
+% \fi
+% } {
+% \endgroup
+% }
+% \else
+% \doifelseMMCfunctioninapply \recurselevel {\MMLcfunctionlist,\MMLcconstructlist} {
+% \MMLcreset
+% } {
+% }
+% \fi
+% } {
+% }
+% \getXMLstackdata\recurselevel
+% \ifnum\recurselevel<\XMLstacklevel\relax
+% -
+% \fi
+% \endgroup
+% }
+% \fi
+% \stopxmlsetups
+%
+% \stopmoduletestsection
diff --git a/tex/context/modules/mkiv/x-newmml.mkiv b/tex/context/modules/mkiv/x-newmml.mkiv
new file mode 100644
index 000000000..4c12daeee
--- /dev/null
+++ b/tex/context/modules/mkiv/x-newmml.mkiv
@@ -0,0 +1,16 @@
+%D \module
+%D [ file=x-newmml,
+%D version=2008.05.28,
+%D title=\CONTEXT\ XML Modules,
+%D subtitle=MathML Renderer,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\input x-mathml.mkiv
+
+\endinput
diff --git a/tex/context/modules/mkiv/x-pfs-01.mkiv b/tex/context/modules/mkiv/x-pfs-01.mkiv
new file mode 100644
index 000000000..8b06bd873
--- /dev/null
+++ b/tex/context/modules/mkiv/x-pfs-01.mkiv
@@ -0,0 +1,399 @@
+% pfsense status info, work in progress
+%
+% usage:
+%
+% context --environment=x-pfs-01 filename.xml
+%
+% or
+%
+% <?context-directive job ctxfile x-pfsense.ctx ?>
+
+\setupbodyfont
+ [dejavu,10pt]
+
+\setuplayout
+ [topspace=1cm,
+ backspace=1cm,
+ footer=0pt,
+ header=1cm,
+ width=middle,
+ height=middle]
+
+\setupheader
+ [style=bold]
+
+\setuphead
+ [section]
+ [style=\bfb]
+
+\setuphead
+ [subsection]
+ [style=\bfa]
+
+\setuppagenumbering
+ [location=]
+
+\dontcomplain
+
+% todo: show all values and map keys onto longer names via labels
+
+\startxmlsetups xml:system
+
+ \startsection[title={System}]
+
+ \startsubsection[title={Properties}]
+
+ \starttabulate[|B|T|]
+ \NC hostname \NC \xmlfirst{#1}{hostname} \NC \NR
+ \NC domain \NC \xmlfirst{#1}{domain} \NC \NR
+ \NC timezone \NC \xmlfirst{#1}{timezone} \NC \NR
+ \NC timeservers \NC \xmlfirst{#1}{timeservers} \NC \NR
+ \NC dnsserver \NC \xmlfirst{#1}{dnsserver} \NC \NR
+ \stoptabulate
+
+ \stopsubsection
+
+ \startsubsection[title={Groups}]
+ \xmlfilter{#1}{group/command(xml:system:group)}
+ \stopsubsection
+
+ \startsubsection[title={Users}]
+ \xmlfilter{#1}{user/command(xml:system:user)}
+ \stopsubsection
+
+ \stopsection
+
+\stopxmlsetups
+
+\startxmlsetups xml:system:group
+
+ \starttabulate[|B|T|]
+ \NC name \NC \xmlfirst{#1}{/name} \NC \NR
+ \NC description \NC \xmlfirst{#1}{/descr} \NC \NR
+ \NC scope \NC \xmlfirst{#1}{/scope} \NC \NR
+ \NC gid \NC \xmlfirst{#1}{/gid} \NC \NR
+ \NC privilege \NC \xmlfirst{#1}{/priv} \NC \NR
+ \NC members \NC \xmlconcat{#1}{/member}{ } \NC \NR
+ \stoptabulate
+
+\stopxmlsetups
+
+\startxmlsetups xml:system:user
+
+ \starttabulate[|B|T|]
+ \NC name \NC \xmlfirst{#1}{/name} \NC \NR
+ \NC description \NC \xmlfirst{#1}{/descr} \NC \NR
+ \NC scope \NC \xmlfirst{#1}{/scope} \NC \NR
+ \NC uid \NC \xmlfirst{#1}{/uid} \NC \NR
+ \NC group \NC \xmlfirst{#1}{/groupname} \NC \NR
+ \NC privilege \NC \xmlfirst{#1}{/priv} \NC \NR
+ \NC password \NC \xmldoifelsetext{#1}{/password}{set}{unset} \NC \NR
+ \NC ipsec psk \NC \xmldoifelsetext{#1}{/ipsecpsk}{set}{unset} \NC \NR
+ \NC certificate \NC \xmldoifelsetext{#1}{/cert} {set}{unset} \NC \NR
+ \stoptabulate
+
+\stopxmlsetups
+
+\startxmlsetups xml:interfaces
+
+ \startsection[title={Interfaces}]
+
+ \xmlfilter{#1}{*/command(xml:interfaces:network)}
+
+ \stopsection
+
+\stopxmlsetups
+
+\startxmlsetups xml:interfaces:network
+
+ \startsubsection[title={\xmltag{#1}}]
+
+ % <blockpriv/> <blockbogons/> <spoofmac/> <enable/>
+
+ \starttabulate[|B|T|]
+ \NC interface \NC \xmlfirst{#1}{/if} \NC \NR
+ \NC block private \NC \xmldoifelse{#1}{/blockpriv} {yes}{no} \NC \NR
+ \NC block bogons \NC \xmldoifelse{#1}{/blockbogons}{yes}{no} \NC \NR
+ \NC spoof mac address \NC \xmldoifelse{#1}{/spoofmac} {yes}{no} \NC \NR
+ \NC enable interface \NC \xmldoifelse{#1}{/enable} {yes}{no} \NC \NR
+ \NC ipaddress \NC \xmlfirst{#1}{/ipaddr} \NC \NR
+ \NC subnet \NC \xmlfirst{#1}{/subnet} \NC \NR
+ \NC gateway \NC \xmlfirst{#1}{/gateway} \NC \NR
+ \NC description \NC \xmlfirst{#1}{/descr} \NC \NR
+ \stoptabulate
+
+ \stopsubsection
+
+\stopxmlsetups
+
+\startxmlsetups xml:gateways
+
+ \startsection[title={Gateways}]
+
+ \xmlfilter{#1}{*/command(xml:gateways:entry)}
+
+ \stopsection
+
+\stopxmlsetups
+
+\startxmlsetups xml:gateways:entry
+
+ \starttabulate[|B|T|]
+ \NC interface \NC \xmlfirst{#1}{/interface} \NC \NR
+ \NC gateway \NC \xmlfirst{#1}{/gateway} \NC \NR
+ \NC name \NC \xmlfirst{#1}{/name} \NC \NR
+ \NC weight \NC \xmlfirst{#1}{/weight} \NC \NR
+ \NC interval \NC \xmlfirst{#1}{/interval} \NC \NR
+ \NC description \NC \xmlfirst{#1}{/descr} \NC \NR
+ \NC disable monitor \NC \xmlfirst{#1}{/monitor_disable} \NC \NR
+ \NC default gateway \NC \xmlfirst{#1}{/defaultgw} \NC \NR
+ \stoptabulate
+
+\stopxmlsetups
+
+\startxmlsetups xml:virtualips
+
+ \startsection[title={Virtual ipadresses}]
+
+ \xmlfilter{#1}{*/command(xml:virtualips:entry)}
+
+ \stopsection
+
+\stopxmlsetups
+
+\startxmlsetups xml:virtualips:entry
+
+ \starttabulate[|B|T|]
+ \NC interface \NC \xmlfirst{#1}{/interface} \NC \NR
+ \NC type \NC \xmlfirst{#1}{/type} \NC \NR
+ \NC mode \NC \xmlfirst{#1}{/mode} \NC \NR
+ \NC subnet \NC \xmlfirst{#1}{/subnet}
+ /\xmlfirst{#1}{/subnet_bits} \NC \NR
+ \NC description \NC \xmlfirst{#1}{/descr} \NC \NR
+ \stoptabulate
+
+\stopxmlsetups
+
+\startxmlsetups xml:dhcp
+
+ \startsection[title={DHCP}]
+
+ \xmlfilter{#1}{*/command(xml:dhcp:network)}
+
+ \stopsection
+
+\stopxmlsetups
+
+\startxmlsetups xml:dhcp:network
+
+ \startsubsection[title={\xmltag{#1}}]
+
+ \starttabulate[|B|T|]
+ \NC range \NC \xmlfirst{#1}{/range/from} \endash\ \xmlfirst{#1}{/range/to} \NC \NR
+ \NC domain \NC \xmlfirst{#1}{/domain} \NC \NR
+ \NC dnsserver \NC \xmlfirst{#1}{/dnsserver} \NC \NR
+ \NC gateway \NC \xmlfirst{#1}{/gateway} \NC \NR
+ \NC ddnsdomain \NC \xmlfirst{#1}{/ddnsdomain} \NC \NR
+ \stoptabulate
+
+ \xmldoif {#1} {/staticmap} {
+
+ \starttabulate[|T|T|T|p|]
+ \NC \rm\bf macaddress
+ \NC \rm\bf ipaddress
+ \NC \rm\bf hostname
+ \NC \rm\bf description
+ \NC \NR
+ \HL
+ \xmlfilter{#1}{/staticmap/command(xml:dhcp:network:entry)}
+ \stoptabulate
+
+ }
+
+ \stopsubsection
+
+\stopxmlsetups
+
+\startxmlsetups xml:dhcp:network:entry
+
+ \NC \xmlfirst{#1}{/mac}
+ \NC \xmlfirst{#1}{/ipaddr}
+ \NC \xmlfirst{#1}{/hostname}
+ \NC \xmlfirst{#1}{/descr}
+ \NC \NR
+
+\stopxmlsetups
+
+\startxmlsetups xml:dnsmasq
+
+ \startsection[title={DNS MASQ}]
+
+ \starttabulate[|T|T|T|p|]
+ \NC \rm\bf host
+ \NC \rm\bf domain
+ \NC \rm\bf ipaddress
+ \NC \rm\bf description
+ \NC \NR
+ \HL
+ \xmlfilter{#1}{/hosts/command(xml:dnsmasq:hosts)}
+ \stoptabulate
+
+ \stopsection
+
+\stopxmlsetups
+
+
+\startxmlsetups xml:dnsmasq:hosts
+
+ \NC \xmlfirst{#1}{/host}
+ \NC \xmlfirst{#1}{/domain}
+ \NC \xmlfirst{#1}{/ip}
+ \NC \xmlfirst{#1}{/descr}
+ \NC \NR
+
+\stopxmlsetups
+
+\startxmlsetups xml:nat
+
+ \startsection[title={NAT}]
+
+ \startsubsection[title={Rules}]
+
+ \starttabulate[|T|T|T|T|T|p|]
+ \NC \rm\bf interface
+ \NC \rm\bf protocol
+ \NC \rm\bf source
+ \NC \rm\bf destination
+ \NC \rm\bf target
+ \NC \rm\bf description
+ \NC \NR
+ \HL
+ \xmlfilter{#1}{/rule/command(xml:nat:rule)}
+ \stoptabulate
+
+ \stopsubsection
+
+ \startsubsection[title={One to one}]
+
+ \starttabulate[|T|T|T|T|T|p|]
+ \NC \rm\bf interface
+ \NC \rm\bf protocol
+ \NC \rm\bf source
+ \NC \rm\bf destination
+ \NC \rm\bf external
+ \NC \rm\bf description
+ \NC \NR
+ \HL
+ \xmlfilter{#1}{/onetoone/command(xml:nat:onetoone)}
+ \stoptabulate
+
+ \stopsubsection
+
+\stopsection
+
+\stopxmlsetups
+
+\startxmlsetups xml:nat:rule
+
+ \NC \xmlfilter{#1}{/interface/command(xml:checked)}
+ \NC \xmlfilter{#1}{/protocol/command(xml:checked)}
+ \NC \xmlfilter{#1}{/source/command(xml:checked)}
+ \NC \xmlfilter{#1}{/destination/(address|any)/command(xml:checked)}
+ :\xmlfilter{#1}{/destination/port/command(xml:checked)}
+ \NC \xmlfilter{#1}{/target/command(xml:checked)}
+ :\xmlfilter{#1}{/local-port/command(xml:checked)}
+ \NC \xmlfirst {#1}{/descr}
+ \NC \NR
+
+\stopxmlsetups
+
+\startxmlsetups xml:nat:onetoone
+
+ \NC \xmlfilter{#1}{/interface/command(xml:checked)}
+ \NC \xmlfilter{#1}{/protocol/command(xml:checked)}
+ \NC \xmlfilter{#1}{/source/command(xml:checked)}
+ \NC \xmlfilter{#1}{/destination/(address|any)/command(xml:checked)}
+ :\xmlfilter{#1}{/destination/port/command(xml:checked)}
+ \NC \xmlfilter{#1}{/external/command(xml:checked)}
+ :\xmlfilter{#1}{/local-port/command(xml:checked)}
+ \NC \xmlfirst {#1}{/descr}
+ \NC \NR
+
+\stopxmlsetups
+
+\startxmlsetups xml:checked
+ \xmldoifelse {#1} {/any} {
+ *
+ } {
+ \xmldoifelsetext {#1} {.} {
+ \xmlflush{#1}
+ } {
+ *
+ }
+ }
+\stopxmlsetups
+
+\startxmlsetups xml:filter
+
+ \startsection[title={Filter}]
+
+ \startsubsection[title={Rules}]
+
+ \starttabulate[|T|T|T|T|T|p|]
+ \NC \rm\bf type
+ \NC \rm\bf interface
+ \NC \rm\bf protocol
+ \NC \rm\bf source
+ \NC \rm\bf destination
+ \NC \rm\bf description
+ \NC \NR
+ \HL
+ \xmlfilter{#1}{/rule/command(xml:filter:rule)}
+ \stoptabulate
+
+ \stopsubsection
+
+ \stopsubsection
+
+\stopxmlsetups
+
+\startxmlsetups xml:filter:rule
+
+ \NC \xmlfilter{#1}{/type/command(xml:checked)}
+ \NC \xmlfilter{#1}{/interface/command(xml:checked)}
+ \NC \xmlfilter{#1}{/protocol/command(xml:checked)}
+ \NC \xmlfilter{#1}{/source/(address|any)/command(xml:checked)}:
+ :\xmlfilter{#1}{/source/port/command(xml:checked)}
+ \NC \xmlfilter{#1}{/destination/(address|any)/command(xml:checked)}
+ :\xmlfilter{#1}{/destination/port/command(xml:checked)}
+ \NC \xmlfirst {#1}{/descr}
+ \NC \NR
+
+\stopxmlsetups
+
+\starttext
+
+ \doifelse {\inputfilename} {x-pfs-01.mkiv} {
+
+ \xmlloadonly{main}{router.xml}{}
+
+ \setupheadertexts[router.xml][\pagenumber]
+
+ } {
+
+ \xmlloadonly{main}{\inputfilename}{}
+
+ \setupheadertexts[\inputfilename][\pagenumber]
+
+ }
+
+ \xmlfilter{main}{/pfsense/system/command(xml:system)}
+ \xmlfilter{main}{/pfsense/interfaces/command(xml:interfaces)}
+ \xmlfilter{main}{/pfsense/gateways/command(xml:gateways)}
+ \xmlfilter{main}{/pfsense/virtualip/command(xml:virtualips)}
+ \xmlfilter{main}{/pfsense/dhcpd/command(xml:dhcp)}
+ \xmlfilter{main}{/pfsense/dnsmasq/command(xml:dnsmasq)}
+ \xmlfilter{main}{/pfsense/nat/command(xml:nat)}
+ \xmlfilter{main}{/pfsense/filter/command(xml:filter)}
+
+\stoptext
diff --git a/tex/context/modules/mkiv/x-pfsense.ctx b/tex/context/modules/mkiv/x-pfsense.ctx
new file mode 100644
index 000000000..8d7087cf2
--- /dev/null
+++ b/tex/context/modules/mkiv/x-pfsense.ctx
@@ -0,0 +1,15 @@
+<?xml version='1.0' standalone='yes'?>
+
+<!-- this is also a demo of using the xml interface -->
+
+<ctx:job>
+ <ctx:message>pfsense configuration listing</ctx:message>
+ <ctx:process>
+ <ctx:flags>
+ <ctx:flag>purge</ctx:flag>
+ </ctx:flags>
+ <ctx:resources>
+ <ctx:environment>x-pfs-01.mkiv</ctx:environment>
+ </ctx:resources>
+ </ctx:process>
+</ctx:job>
diff --git a/tex/context/modules/mkiv/x-physml.mkiv b/tex/context/modules/mkiv/x-physml.mkiv
new file mode 100644
index 000000000..dd9a164dd
--- /dev/null
+++ b/tex/context/modules/mkiv/x-physml.mkiv
@@ -0,0 +1,16 @@
+%D \module
+%D [ file=m-physml,
+%D version=2001.09.04,
+%D title=\CONTEXT\ XML Modules,
+%D subtitle=Loading PHYSML Filters,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% todo
+
+\endinput
diff --git a/tex/context/modules/mkiv/x-res-01.mkiv b/tex/context/modules/mkiv/x-res-01.mkiv
new file mode 100644
index 000000000..36070c615
--- /dev/null
+++ b/tex/context/modules/mkiv/x-res-01.mkiv
@@ -0,0 +1,427 @@
+%D \module
+%D [ file=x-fig-01,
+%D version=2001.03.21,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Figure Base Generation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D See \type {x-fig-00.tex} and \type {x-fig-04.tex} for more
+%D information on how to use and generate figure databases.
+%D This file loads the file named \type {\jobfilename}
+%D (\TEXEXEC\ will set this variable). You can apply this
+%D style to a database by saying:
+%D
+%D \starttyping
+%D context --input=d-res-01.xml auto:x-res-01.mkiv
+%D \stoptyping
+%D
+%D The following modes are supported (not all yet in mkiv):
+%D
+%D \starttabulate[|lT|l|]
+%D \NC letter \NC map the preview on letter size \NC \NR
+%D \NC compact \NC use an alternative presentation \NC \NR
+%D \NC clipgrid-distance \NC add a copy with grid overlayed \NC \NR
+%D \NC clipgrid-steps \NC add a copy with grid overlayed \NC \NR
+%D \NC previewpage-A4 \NC show graphic relative to A4 \NC \NR
+%D \NC previewpage-letter \NC show graphic relative to letter \NC \NR
+%D \NC previewpage-S6 \NC show graphic relative to S6 \NC \NR
+%D \stoptabulate
+%D
+%D The resulting file has the following characteristics:
+%D
+%D \startitemize[packed]
+%D \startitem the document is split into three sections: first each
+%D figure is shown at its own page, then an overview of figures is
+%D shown with some data alongside, and finally an index and table of
+%D contents shows up \stopitem
+%D \startitem there is no title page, which means that one can access
+%D a figure by page number without offset \stopitem
+%D \startitem the document is opened at the first overview page, that
+%D is, when the viewer supports it \stopitem
+%D \startitem the graphic is shown 3~times: on a page of its own,
+%D scaled to a fixed dimension, and relative to a4 or letter paper
+%D size \stopitem
+%D \startitem the labels can be accessed in an index and list at the
+%D end of the document \stopitem
+%D \stopitemize
+
+\defineregister
+ [figureindex]
+
+\setupregister
+ [figureindex]
+ [criterium=text,
+ interaction=text,
+ pagenumber=no,
+ indicator=no]
+
+\setuptolerance
+ [verytolerant]
+
+\setupbuttons
+ [offset=10pt,
+ width=broad,
+ strut=no,
+ rulethickness=1pt,
+ framecolor=darkred]
+
+\setuplayout
+ [topspace=15pt,
+ backspace=15pt,
+ header=0pt,
+ footer=0pt,
+ bottom=20pt,
+ bottomdistance=10pt,
+ width=middle,
+ height=fit]
+
+\setupbackgrounds
+ [page]
+ [background=,
+ backgroundcolor=gray]
+
+\setupinteractionscreen
+ [width=max,
+ height=max]
+
+\setupinteraction
+ [style=,
+ color=,
+ contrastcolor=,
+ state=start]
+
+\setuphead
+ [section]
+ [style=bfb]
+
+\setupinteractionmenu
+ [bottom]
+ [left=\hfill,
+ middle=\hskip10pt,
+ frame=off,
+ style=bold,
+ background=color,
+ backgroundcolor=darkred,
+ foregroundcolor=white]
+
+\startinteractionmenu[bottom]
+ \but [begin] begin \\
+ \but [index] index \\
+ \but [list] list \\
+ \but [CloseDocument] close \\
+ \but [PreviousJump] go back \\
+\stopinteractionmenu
+
+\setupinteraction
+ [openaction=begin]
+
+\setuppapersize
+ [S6][S6]
+
+\setupbackgrounds
+ [page]
+ [background=color]
+
+\setupinteraction
+ [menu=on]
+
+\setupbodyfont
+ [tt,10pt]
+
+\definesymbol [attachment] [{\strut\bf\color[darkred]{\inputfilename}}] % jobname.xml}}]
+\setupattachments [symbol=attachment,alternative=,location=text]
+\useattachment [datafile] [\inputfilename]
+
+\xmlloadonly{main}{\inputfilename}{}
+
+\mainlanguage[\xmlattributedef{main}{/rlx:library}{language}{en}]
+
+\startxmlsetups xml:resource:asis
+ \startTEXpage[pagestate=start]
+ \xmldoifelsetext{#1}{/rlx:label} {
+ \edef\CurrentLabel{\xmltext{#1}{rlx:label}}
+ } {
+ \edef\CurrentLabel{\xmltext{#1}{rlx:file}}
+ }
+ \pagereference [
+ asis:\CurrentLabel
+ ]
+ \gotobox {
+ \externalfigure[\xmltext{#1}{/rlx:file}]
+ }[% tricky no space before [
+ data:\CurrentLabel
+ ]
+ \stopTEXpage
+\stopxmlsetups
+
+\definemeasure[figure:width] [210mm]
+\definemeasure[figure:height][297mm]
+
+\startmode[letter]
+ \enablemode[previewpage-letter]
+\stopmode
+
+\startmode[previewpage-A4]
+ \definemeasure[figure:width] [210mm]
+ \definemeasure[figure:height][297mm]
+\stopmode
+
+\startmode[previewpage-letter]
+ \definemeasure[figure:width] [8.5in]
+ \definemeasure[figure:height][11in]
+\stopmode
+
+\startmode[previewpage-S6]
+ \definemeasure[figure:width] [600pt]
+ \definemeasure[figure:height][450pt]
+\stopmode
+
+\startxmlsetups xml:resource:a
+ \button
+ [
+ width=150pt,
+ height=100pt,
+ offset=10pt,
+ frame=off,
+ background=color,
+ backgroundcolor=white,
+ color=
+ ]
+ {
+ \externalfigure
+ [\xmltext{#1}{rlx:file}]
+ [factor=max]
+ }
+ [
+ grid:\CurrentLabel
+ ]
+\stopxmlsetups
+
+\startxmlsetups xml:resource:b
+ \framed
+ [
+ width=150pt,
+ height=100pt,
+ offset=10pt,
+ frame=off,
+ background=color,
+ backgroundcolor=white,
+ color=
+ ]
+ {
+ \externalfigure [
+ \xmltext{#1}{rlx:file}
+ ] [
+ factor=max
+ ]
+ }
+\stopxmlsetups
+
+\startxmlsetups xml:resource:data
+
+ % using a layer makes more sense but we had this ...
+
+ \xmldoifelsetext{#1}{/rlx:label} {
+ \edef\CurrentLabel{\xmltext{#1}{rlx:label}}
+ } {
+ \edef\CurrentLabel{\xmltext{#1}{rlx:file}}
+ }
+
+ \button {
+ \hbox to \hsize {
+ \forgetall
+ \dontcomplain
+ \pagereference[data:\CurrentLabel]
+
+ %\ifnum\CurrentPage=1 \pagereference[begin]\fi
+
+ \expanded{\figureindex{\xmltext{#1}{/rlx:label}}}
+
+ \vbox to 100pt {
+ \hsize30pt
+ \vskip5pt
+ \hbox to \hsize {
+ \hss
+ \strut
+ \bf
+ \at[asis:\CurrentLabel]
+ \hss
+ }
+ \vfill
+ }
+ \advance\hsize by -30pt
+ \doifelsemode {clipgrid-distance,clipgrid-steps} {
+ \xmlsetup{#1}{xml:resource:a}
+ } {
+ \xmlsetup{#1}{xml:resource:b}
+ }
+ \edef\CurrentWidth {\the\dimexpr\figurenaturalwidth}
+ \edef\CurrentHeight{\the\dimexpr\figurenaturalheight}
+ \advance\hsize by -150pt
+ \hskip10pt
+ \advance\hsize by -10pt
+ \vbox to 100pt {
+ \hsize40pt
+ \framed [
+ offset=overlay,
+ framecolor=darkred,
+ rulethickness=.5pt
+ ] {
+ \scale [
+ width=40pt
+ ] {
+ \framed [
+ width=\measure{figure:width},
+ height=\measure{figure:height},
+ offset=overlay,
+ frame=off,
+ background=color,
+ backgroundcolor=white
+ ] {
+ \externalfigure
+ [
+ \xmltext{#1}{rlx:file}
+ ] [
+ reset=yes
+ ]
+ }
+ }
+ }
+ \vfill
+ }
+ \advance\hsize by -40pt
+ \hskip10pt
+ \advance\hsize by -10pt
+ \vbox to 100pt {
+ \blank[disable]
+ \starttabulate[|Bel|p|]
+ \NC file \NC \xmltext{#1}{/rlx:file} \NC \NR
+ \xmldoif{#1}{/rlx:label} {\NC label \NC \xmltext{#1}{/rlx:label} \NC \NR}
+ \NC dimensions \NC \CurrentWidth\ * \CurrentHeight \NC \NR
+ \xmldoif{#1}{/rlx:copyright} {\NC copyright \NC \xmltext{#1}{/rlx:copyright} \NC \NR}
+ \xmldoif{#1}{/rlx:status} {\NC status \NC \xmltext{#1}{/rlx:status} \NC \NR}
+ \xmldoif{#1}{/rlx:comment} {\NC comment \NC \xmltext{#1}{/rlx:comment} \NC \NR}
+ \stoptabulate
+ \vfill
+ }
+ }
+ } [
+ asis:\CurrentLabel
+ ]
+
+ \vskip10pt
+
+\stopxmlsetups
+
+\startxmlsetups xml:description
+
+ \starttabulate[|lBe|p|]
+ \xmldoif{#1}{/rlx:organization} {\NC organization \NC \xmltext{#1}{/rlx:organization} \NC \NR}
+ \xmldoif{#1}{/rlx:project} {\NC project \NC \xmltext{#1}{/rlx:project} \NC \NR}
+ \xmldoif{#1}{/rlx:product} {\NC product \NC \xmltext{#1}{/rlx:product} \NC \NR}
+ \xmldoif{#1}{/rlx:comment} {\NC comment \NC \xmltext{#1}{/rlx:comment} \NC \NR}
+ \NC specification \NC \attachment[datafile] \NC \NR
+ \stoptabulate
+
+\stopxmlsetups
+
+\starttext
+
+ \xmlfilter{main}{/rlx:library/rlx:resource/command(xml:resource:asis)}
+
+ \subject {Figure collection}
+
+ \xmlfilter{main}{/rlx:library/rlx:description/command(xml:description)}
+
+ \subject [list] {List of figures}
+
+ \xmlfilter{main}{/rlx:library/rlx:resource/command(xml:resource:data)}
+
+ \page
+
+ \subject [index] {Index of figures}
+
+ \startcolumns
+ \placeregister[figureindex]
+ \stopcolumns
+
+\stoptext
+
+% \doifmodeelse{clipgrid-distance,clipgrid-steps}{\page}{\stoptext}
+
+% \startuniqueMPgraphic{clipgrid}{dx,dy,nx,ny,type}
+% numeric gdx, gdy, lbx, lby ;
+% if \MPvar{type}=1 :
+% gdx := \MPvar{dy} ;
+% gdy := \MPvar{dx} ;
+% else :
+% gdx := OverlayWidth /\MPvar{nx} ;
+% gdy := OverlayHeight/\MPvar{ny} ;
+% fi ;
+% lbx := gdx ;
+% lby := gdy ;
+% defaultfont := "\truefontname{Mono}" ;
+% defaultscale := .5 ;
+% numeric pen ; pen := .25pt ;
+% def MyGrid text t =
+% draw vlingrid (0,OverlayWidth ,gdy,OverlayWidth ,OverlayHeight) t ;
+% draw hlingrid (0,OverlayHeight,gdx,OverlayHeight,OverlayWidth ) t ;
+% enddef ;
+% pickup pencircle scaled pen ;
+% MyGrid withcolor white ;
+% MyGrid dashed evenly scaled pen ;
+% draw OverlayBox withcolor white ;
+% draw OverlayBox dashed evenly scaled pen ;
+% draw vlinlabel.bot(0,eps+OverlayWidth /lby,2,OverlayWidth ) ;
+% draw hlinlabel.lft(0,eps+OverlayHeight/lbx,2,OverlayHeight) ;
+% setbounds currentpicture to OverlayBox enlarged (2*EmWidth) ;
+% \stopuniqueMPgraphic
+
+% \presetMPvariable[clipgrid][dx=10pt]
+% \presetMPvariable[clipgrid][dy=10pt]
+% \presetMPvariable[clipgrid][nx=10]
+% \presetMPvariable[clipgrid][ny=10]
+
+% \startmode[clipgrid-distance]
+% \defineoverlay[grid][\uniqueMPgraphic{clipgrid}{type=1}]
+% \stopmode
+
+% \startmode[clipgrid-steps]
+% \defineoverlay[grid][\uniqueMPgraphic{clipgrid}{type=2}]
+% \stopmode
+
+% \setupexternalfigures
+% [background={color,foreground,grid},
+% backgroundcolor=white]
+
+% \def\StartFigureD
+% {\StartFigureA}
+
+% \def\StopFigureD
+% {\doglobal\increment\CurrentPage
+% \setupbackgrounds[page][background=page]
+% \startpagefigure[\XMLflush{rlx:file}][offset=20pt]%
+% \doifelsenothing{\XMLflush{rlx:label}}
+% {\expanded{\definereference[Description][about:\XMLflush{rlx:file}]}%
+% \expanded{\pagereference[grid:\XMLflush{rlx:file}]}}
+% {\expanded{\definereference[Description][about:\XMLflush{rlx:label}]}%
+% \expanded{\pagereference[grid:\XMLflush{rlx:label}]}}
+% \stoppagefigure
+% %\pagefigure[\XMLflush{rlx:file}][offset=20pt]
+% \setupbackgrounds[page][background=]
+% \egroup}
+
+% \defineXMLignore [rlx:description]
+% \defineXMLenvironment [rlx:figure] \StartFigureD \StopFigureD
+
+% \doglobal\newcounter\CurrentPage
+
+% \processXMLfilegrouped{\jobfullname} \page
+
+\stoptext
diff --git a/tex/context/modules/mkiv/x-res-50.mkiv b/tex/context/modules/mkiv/x-res-50.mkiv
new file mode 100644
index 000000000..62a86b7a7
--- /dev/null
+++ b/tex/context/modules/mkiv/x-res-50.mkiv
@@ -0,0 +1,431 @@
+%D \module
+%D [ file=x-res-50,
+%D version=2004.02.18,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Multimedia Presentation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D The xml mapping has to be redone!
+
+\endinput
+
+%D This is a preliminary module, using a preliminary xml media format that
+%D looks as follows (record is embedded in resource library element):
+%D
+%D \starttyping
+%D <rl:mediaclip label="sample 1">
+%D <rl:name>Sample One</rl:name>
+%D <rl:mime>application/x-shockwave-flash</rl:mime>
+%D <rl:file>http://localhost/mb.swf</rl:file>
+%D <rl:width>8cm</rl:width>
+%D <rl:height>6cm</rl:height>
+%D <rl:text>Nothing special to be said.</rl:text>
+%D <rl:picture>cow.pdf</rl:picture>
+%D </rl:mediaclip>
+%D
+%D <rl:mediaclip label="sample 2">
+%D <rl:name>Sample Two</rl:name>
+%D <rl:mime>audio/mpeg</rl:mime>
+%D <rl:file>mb.mp3</rl:file>
+%D <rl:picture>mb.jpg</rl:picture>
+%D </rl:mediaclip>
+%D \stoptyping
+%D
+%D \starttyping
+%D texexec --pdf --use=med-show yourfile.xml
+%D \stoptyping
+%D
+%D Bonus:
+%D
+%D \starttyping
+%D --arg="url=http://localhost:8881/e:/media"
+%D \stoptyping
+
+% output=pdftex
+
+% \nopdfcompression
+
+% bugs in recognizing embedded stream cq. player
+% bugs in layers + hide/vide
+% bugs in save javascripts
+% bugs all over the place
+
+% in principe kan menu overal hetzelfde zijn als we via JS per pagina de clip var zetten,
+% hoewel, misschien zal het menu gaan afhangen van de soort clip
+
+% viewerlayer (eigenschap) aan framed en layer
+
+\usemodule[meta-dum] \usemodule[contml] \autoXMLnamespace [context]
+
+\doifelsevariable{environment}{url}
+ {\setvariables[mediaclip][url=\getvariable{environment}{url}/]}
+ {\setvariables[mediaclip][url=]}
+
+\startmode [silent]
+
+ \setvariables[mediaclip:option][start=]
+
+\stopmode
+
+\startnotmode [silent]
+
+ \setvariables[mediaclip:option][start=auto]
+
+\stopnotmode
+
+\chardef\XMLtokensreduction=1 \dontcomplain % \showframe \pdfcompresslevel=0
+
+\setuppapersize
+ [S6][S6]
+
+\definemeasure [GapSize] [\dimexpr( 15pt)]
+\definemeasure [EdgeWidth] [\dimexpr(100pt)]
+\definemeasure [TextWidth] [\dimexpr(.5\textwidth)]
+\definemeasure [RenderingWidth] [\dimexpr(\textwidth)]
+\definemeasure [RenderingHeight] [\dimexpr(\textheight)]
+
+% \XMLflush{rl:ratio}\dimexpr(.75\textwidth),
+
+\setuplayout
+ [backspace=\measure{GapSize},
+ topspace=\measure{GapSize},
+ header=0pt,
+ footer=0pt,
+ margin=0pt,
+ edgedistance=2\measure{GapSize},
+ rightedge=\measure{EdgeWidth},
+ bottomdistance=2\measure{GapSize},
+ bottom=2\measure{GapSize},
+ height=fit,
+ width=fit]
+
+\setupinteraction
+ [state=start,
+ color=lightgray,
+ contrastcolor=lightgray,
+ openaction=PresetFields,
+ closeaction=ForgetChanges,
+ menu=on,
+ click=no]
+
+\setupinteractionscreen
+ [option=max]
+
+\setupcolors
+ [state=start]
+
+\usetypescript
+ [palatino][texnansi]
+
+\setupbodyfont
+ [palatino]
+
+\definecolor[darkgray] [s=.2]
+\definecolor[mediumgray][s=.5]
+\definecolor[lightgray] [s=.8]
+\definecolor[transgray] [s=1,t=.9,a=1]
+
+\setupbackgrounds
+ [page]
+ [backgroundoffset=\measure{GapSize},
+ background={color,pagebutton},
+ backgroundcolor=black]
+
+\definelayer
+ [main]
+ [width=\textwidth,
+ height=\textheight]
+
+\definelayer
+ [extra]
+ [width=\rightedgewidth,
+ height=\bottomheight]
+
+\setupbackgrounds
+ [text]
+ [backgroundoffset=0pt,
+ background=main]
+
+\setupbackgrounds
+ [bottom][rightedge]
+ [backgroundoffset=0pt,
+ background=extra]
+
+% java scripts
+
+\startJSpreamble {handy} used now
+
+ function ForgetChanges ()
+ { this.dirty = false }
+
+ function PresetFields ()
+ { this.syncAnnotScan() }
+
+\stopJSpreamble
+
+\definereference[PresetFields] [JS(PresetFields)]
+\definereference[ForgetChanges][JS(ForgetChanges)]
+
+% layers
+
+\defineviewerlayer [menulayer] [title=menulayer]
+\defineviewerlayer [textlayer] [title=textlayer,state=stop]
+\defineviewerlayer [datalayer] [title=datalayer,state=stop]
+
+\setupfield
+ [rollbutton]
+ [fieldlayer=menulayer]
+
+\setupfield
+ [rollbutton]
+ [option=auto]
+
+\definepalet
+ [rollover]
+ [n=darkgray,
+ r=lightgray,
+ d=darkgray]
+
+% list
+
+\definelist
+ [clips]
+ [expansion=yes,
+ criterium=text,
+ alternative=f]
+
+% navigation
+
+\defineoverlay[pagebutton][\overlaybutton{HideLayer{textlayer},HideLayer{datalayer},ToggleLayer{menulayer}}]
+\defineoverlay[textbutton][\overlaybutton{HideLayer{textlayer},HideLayer{datalayer}}]
+\defineoverlay[databutton][\overlaybutton{HideLayer{textlayer},HideLayer{datalayer}}]
+
+% clips
+
+\defineXMLenvironment
+ [rl:mediaclip]
+ {\setups[mediaclip:start]}
+ {\setups[mediaclip:stop]}
+
+\newcounter\MediaClip
+
+\startsetups[mediaclip:start]
+
+ \bgroup \startXMLignore
+
+ % no \startstandardmakeup here since we need the dsta in the menuconstruction
+
+ \defineXMLsave [rl:name]
+ \defineXMLsave [rl:visualization]
+ \defineXMLsave [rl:file]
+ \defineXMLsave [rl:mime]
+ \defineXMLsave [rl:picture] [backgroundcolor=lightgray]
+
+ \defineXMLsavecontent [rl:text] {No additional info.}
+ \defineXMLsavecontent [rl:width] {\measure{RenderingWidth}}
+ \defineXMLsavecontent [rl:height] {\measure{RenderingHeight}}
+ \defineXMLsavecontent [rl:aspect] {1}
+
+\stopsetups
+
+\startsetups[mediaclip:stop]
+
+ \startstandardmakeup
+
+ \doifXMLdataelse{rl:file}
+ {\setups[mediaclip:file:yes]}
+ {}
+
+ \doifXMLdataelse{rl:picture}
+ {\doifelse{\XMLflush{rl:picture}}{self}
+ {\setups[mediaclip:picture:self]}
+ {\setups[mediaclip:picture:yes]}}
+ {\setups[mediaclip:picture:no]}
+
+ \doifXMLdata{rl:text}
+ {\setups[mediaclip:text]}
+
+ \setlayerframed
+ [extra]
+ [preset=rightbottom]
+ [frame=off,offset=overlay,width=fit,background=databutton,align=left]
+ {\startviewerlayer[datalayer]\setups[mediaclip:data]\stopviewerlayer}
+
+ \doifXMLdataelse{rl:name}
+ {\writetolist[clips]{}{\XMLflush{rl:name}}}
+ {\writetolist[clips]{}{\XMLpar{rl:mediaclip}{label}{unknown}}}
+
+ \stopstandardmakeup
+
+ \stopXMLignore \egroup
+
+\stopsetups
+
+\setuptabulate
+ [before=,
+ after=]
+
+\def\rlCleanupFileName#1%
+ {\bgroup
+ \def\cleanup##1##2{\ifnum##1##2=20 \space\else\char\octnumber{##1##2}\fi}%
+ \defineactivecharacter 37 {\cleanup}%
+ \scantokens{#1}%
+ \egroup}
+
+\startsetups[mediaclip:data]
+
+ \noindent \buttonframed
+ [framecolor=lightgray,
+ foregroundcolor=lightgray]
+ {\bf\expanded{\rlCleanupFileName{\XMLflush{rl:file}}}}
+
+ \vskip.75\measure{GapSize}
+
+ \noindent \buttonframed
+ [framecolor=lightgray,
+ foregroundcolor=lightgray]
+ {\bf\XMLflush{rl:mime}}
+
+\stopsetups
+
+\startsetups[mediaclip:picture:self]
+
+ \definerenderingwindow
+ [mediaclip]
+ [width=\XMLflush{rl:width},
+ height=\XMLflush{rl:height},
+ frame=off,
+ openpageaction=StartCurrentRendering,
+ closepageaction=StopCurrentRendering]
+
+ \setlayer
+ [main]
+ {\placerenderingwindow[mediaclip][mediaclip-\MediaClip]}
+
+\stopsetups
+
+\startsetups[mediaclip:picture:yes]
+
+ \setlayer
+ [main]
+ {\externalfigure
+ [\XMLflush{rl:picture}]
+ [background=color,
+ backgroundcolor=\XMLpar{rl:picture}{backgroundcolor}{lightgray},
+ factor=max,
+ width=\XMLflush{rl:width},
+ height=\XMLflush{rl:height}]}
+
+\stopsetups
+
+\startsetups[mediaclip:picture:no]
+
+ \setlayer
+ [main]
+ {\externalfigure
+ [dummy]
+ [width=\XMLflush{rl:width},
+ height=\XMLflush{rl:height}]}
+
+\stopsetups
+
+\startsetups[mediaclip:file:yes]
+
+ \doglobal\increment\MediaClip
+
+ \useexternalrendering
+ [mediaclip-\MediaClip]
+ [\XMLflush{rl:mime}]
+ [\getvariable{mediaclip}{url}\XMLflush{rl:file}]
+ [\getvariable{mediaclip:option}{start}]
+
+\stopsetups
+
+\defineinteractionmenu
+ [navigation] [right]
+
+\defineinteractionmenu
+ [control] [bottom]
+
+\setupinteractionmenu
+ [navigation,control]
+ [state=start,
+ frame=on,
+ middle=\hskip.5\measure{GapSize},
+ inbetween=\vskip.5\measure{GapSize}]
+
+\setupinteractionmenu
+ [right,bottom]
+ [distance=overlay]
+
+\startinteractionmenu [navigation]
+ \rob [HideLayer{textlayer},FirstPage] First Page \\
+ \rob [HideLayer{textlayer},PreviousPage] Previous Page \\
+ \rob [HideLayer{textlayer},NextPage] Next Page \\
+ \rob [HideLayer{textlayer},LastPage] Last Page \\
+ \rob [HideLayer{textlayer},clips] List Of Clips \\
+ \rob [ForgetChanges,CloseDocument] Close Document \\
+\stopinteractionmenu
+
+\startinteractionmenu [control]
+ \rob [StartRendering{mediaclip-\MediaClip}] Start \\
+ \rob [StopRendering{mediaclip-\MediaClip}] Stop \\
+ \rob [PauseRendering{mediaclip-\MediaClip}] Pause \\
+ \rob [ResumeRendering{mediaclip-\MediaClip}] Resume \\
+ \rob [ToggleLayer{datalayer}] Info \\
+ \doifXMLdata{rl:text}{\rob [HideLayer{datalayer},ToggleLayer{textlayer}] Text \\}
+\stopinteractionmenu
+
+\startsetups[mediaclip:text]
+
+ \setlayer
+ [extra]
+ [preset=rightbottom]
+ {\startviewerlayer[textlayer]
+ \framed
+ [align=normal,
+ frame=off,
+ width=\measure{TextWidth},
+ foregroundcolor=darkgray,
+ background={color,textbutton},
+ backgroundcolor=lightgray]
+ {\XMLflush{rl:text}}
+ \stopviewerlayer}
+
+\stopsetups
+
+\setupcolors[textcolor=lightgray]
+
+\startsetups [library:start]
+
+ \starttext
+
+ \setupinteractionmenu[control][state=stop]
+
+ \title[clips]{List of Media Clips}
+
+ \placelist[clips] \page
+
+ \setupinteractionmenu[control][state=start]
+
+\stopsetups
+
+\startsetups [library:stop]
+
+ \stoptext
+
+\stopsetups
+
+\defineXMLenvironment [rl:resourcelibrary]
+ {\setups[library:start]}
+ {\setups[library:stop]}
+
+\doifelsenothing{\inputfilename}
+ {\processXMLfile{mediaclient.xml}}
+ {\processXMLfile{\inputfilename}}
diff --git a/tex/context/modules/mkiv/x-set-11.mkiv b/tex/context/modules/mkiv/x-set-11.mkiv
new file mode 100644
index 000000000..c82b9735d
--- /dev/null
+++ b/tex/context/modules/mkiv/x-set-11.mkiv
@@ -0,0 +1,859 @@
+%D \module
+%D [ file=x-set-11,
+%D version=2004.10.31,
+%D remark=setupx.tex: 1998.07.20 and later,
+%D title=\CONTEXT\ Setup Definitions,
+%D subtitle=Macro Definitions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% we can make this module a bit cleaner using more recent features
+% like sorting the xml directly
+
+% \startluacode
+% collectgarbage("stop")
+% function collectgarbage() return 0 end
+% \stopluacode
+
+% todo: for fun: pure lua interface, but as this style evolved over 15 years
+% it's a waste of time
+%
+% todo:
+%
+% \setup{setupinterlinespace}
+% \setup{setupinterlinespace:1}
+% \setup{setupinterlinespace:2}
+%
+% cd:include -> filename
+% cd:choice
+%
+% register, interaction
+
+\startmessages dutch library: setup
+ title: setup
+ formula: formule
+ number: getal
+ list: lijst
+ dimension: maat
+ mark: markering
+ reference: verwijzing
+ command: commando
+ file: file
+ name: naam
+ identifier: naam
+ text: tekst
+ section: sectie
+ singular: naam enkelvoud
+ plural: naam meervoud
+ matrix: n*m
+ see: zie
+ inherits: erft van
+ 1: de karakters < en > zijn globaal actief!
+ 2: -- wordt verwerkt
+ 3: -- is niet gedefinieerd
+ 4: -- wordt nogmaals verwerkt
+ optional: opt
+ displaymath: formule
+ index: ingang
+ math: formule
+ nothing: leeg
+ file: file
+ position: positie
+ reference: verwijzing
+ csname: naam
+ destination: bestemming
+ triplet: triplet
+ word: woord
+ content: tekst
+\stopmessages
+
+\startmessages english library: setup
+ title: setup
+ formula: formula
+ number: number
+ list: list
+ dimension: dimension
+ mark: mark
+ reference: reference
+ command: command
+ file: file
+ name: name
+ identifier: identifier
+ text: text
+ section: section
+ singular: singular name
+ plural: plural name
+ matrix: n*m
+ see: see
+ inherits: inherits from
+ 1: the characters < and > are globally active!
+ 2: -- is processed
+ 3: -- is undefined
+ 4: -- is processed again
+ optional: opt
+ displaymath: formula
+ index: entry
+ math: formula
+ nothing: empty
+ file: file
+ position: position
+ reference: reference
+ csname: name
+ destination: destination
+ triplet: triplet
+ word: word
+ content: text
+\stopmessages
+
+\startmessages german library: setup
+ title: Setup
+ formula: Formel
+ number: Nummer
+ list: Liste
+ dimension: Dimension
+ mark: Beschriftung
+ reference: Referenz
+ command: Befehl
+ file: Datei
+ name: Name
+ identifier: Name
+ text: Text
+ section: Abschnitt
+ singular: singular
+ plural: plural
+ matrix: n*m
+ see: siehe
+ inherits: inherits from
+ 1: Die Zeichen < und > gelten global!
+ 2: -- wird verarbeitet
+ 3: -- ist undefiniert
+ 4: -- ist mehrmals verarbeitet
+ optional: opt
+ displaymath: formula
+ index: entry
+ math: formula
+ nothing: empty
+ file: file
+ position: position
+ reference: reference
+ csname: name
+ destination: destination
+ triplet: triplet
+ word: word
+ content: text
+\stopmessages
+
+\startmessages czech library: setup
+ title: setup
+ formula: rovnice
+ number: cislo
+ list: seznam
+ dimension: dimenze
+ mark: znacka
+ reference: reference
+ command: prikaz
+ file: soubor
+ name: jmeno
+ identifier: jmeno
+ text: text
+ section: sekce
+ singular: jmeno v singularu
+ plural: jmeno v pluralu
+ matrix: n*m
+ see: viz
+ inherits: inherits from
+ 1: znaky < a > jsou globalne aktivni!
+ 2: -- je zpracovano
+ 3: -- je nedefinovano
+ 4: -- je zpracovano znovu
+ optional: opt
+ displaymath: formula
+ index: entry
+ math: formula
+ nothing: empty
+ file: file
+ position: position
+ reference: reference
+ csname: name
+ destination: destination
+ triplet: triplet
+ word: word
+ content: text
+\stopmessages
+
+\startmessages italian library: setup
+ title: setup
+ formula: formula
+ number: number
+ list: list
+ dimension: dimension
+ mark: mark
+ reference: reference
+ command: command
+ file: file
+ name: name
+ identifier: name
+ text: text
+ section: section
+ singular: singular name
+ plural: plural name
+ matrix: n*m
+ see: see
+ inherits: inherits from
+ 1: the characters < and > are globally active!
+ 2: -- is processed
+ 3: -- is undefined
+ 4: -- is processed again
+ optional: opt
+ displaymath: formula
+ index: entry
+ math: formula
+ nothing: empty
+ file: file
+ position: position
+ reference: reference
+ csname: name
+ destination: destination
+ triplet: triplet
+ word: word
+ content: text
+\stopmessages
+
+\startmessages romanian library: setup
+ title: setari
+ formula: formula
+ number: numar
+ list: lista
+ dimension: dimensiune
+ mark: marcaj
+ reference: referinta
+ command: comanda
+ file: fisier
+ name: nume
+ identifier: nume
+ text: text
+ section: sectiune
+ singular: nume singular
+ plural: nume pluram
+ matrix: n*m
+ see: vezi
+ inherits: inherits from
+ 1: caracterele < si > sunt active global!
+ 2: este procesat --
+ 3: -- este nedefinit
+ 4: -- este procesat din nou
+ optional: opt
+ displaymath: formula
+ index: entry
+ math: formula
+ nothing: empty
+ file: file
+ position: position
+ reference: reference
+ csname: name
+ destination: destination
+ triplet: triplet
+ word: word
+ content: text
+\stopmessages
+
+\startmessages french library: setup
+ title: réglage
+ formula: formule
+ number: numéro
+ list: liste
+ dimension: dimension
+ mark: marquage
+ reference: reference
+ command: commande
+ file: fichier
+ name: nom
+ identifier: identificateur
+ text: texte
+ section: section
+ singular: nom singulier
+ plural: nom pluriel
+ matrix: n*m
+ see: vois
+ inherits: herite de
+ 1: les caractères < et > sont globalement actifs !
+ 2: -- est traité
+ 3: -- n'est pas défini
+ 4: -- est traité de nouveau
+ optional: opt
+ displaymath: formule
+ index: entrée
+ math: formule
+ nothing: vide
+ file: fichier
+ position: position
+ reference: réference
+ csname: nom
+ destination: destination
+ triplet: triplet
+ word: mot
+ content: texte
+\stopmessages
+
+\unprotect
+
+% general
+
+\unexpanded\def\setupnumfont {}
+\unexpanded\def\setuptxtfont {}
+\unexpanded\def\setupintfont {\WORD}
+\unexpanded\def\setupvarfont {\sl}
+\unexpanded\def\setupoptfont {\sl}
+\unexpanded\def\setupalwcolor{}
+\unexpanded\def\setupoptcolor{darkgray}
+
+\def\c!setup!definereserved#1#2%
+ {\setvalue{c!setup!:r:#1}{#2}}
+
+\def\c!setup!reserved!#1%
+ {\executeifdefined{c!setup!:r:#1}{#1}}
+
+\def\c!setup!internal!#1%
+ {\dontleavehmode
+ \begingroup
+ \setupintfont{#1}%
+ \endgroup}
+
+\def\c!setup!text!#1%
+ {\dontleavehmode
+ \begingroup
+ \setupvarfont{#1}%
+ \endgroup}
+
+\def\c!setup!command!#1%
+ {{\setupvarfont{\texescape...#1}}}
+
+\def\??stp{@@stp}
+
+\defineregister
+ [texmacro]
+% [texmacros]
+
+\definesorting
+ [texcommand]
+% [texcommands]
+
+\setupsorting
+ [texcommand]
+ [\c!command=\showsetupinlist,
+ \c!criterium=\@@stpcriterium]
+
+\pushmacro\setuptext
+
+\defineframedtext
+ [setuptext]
+ [\c!width=\hsize,
+ \c!height=\v!fit,
+ \c!align=\v!right,
+ \c!offset=0.75em]
+
+\popmacro\setuptext
+
+%D Loading:
+
+\let\currentSETUPfullname\s!unknown
+
+\startxmlsetups xml:setups:assemblename
+ \doifelse {\xmlatt{#1}{type}} {environment} {
+ \let\currentSETUPprefix\e!start
+ } {
+ \let\currentSETUPprefix\empty
+ }
+ % \edef\currentSETUPname{\xmlatt{#1}{name}}
+ \edef\currentSETUPname{\xmlattribute{#1}{/sequence/string[1]}{value}}%
+ \doifelse {\xmlatt{#1}{generated}} {yes} {
+ \def\currentSETUPgenerated{*}
+ } {
+ \let\currentSETUPgenerated\empty
+ }
+ \doifelsenothing {\xmlatt{#1}{variant}} {
+ \let\currentSETUPvariant\empty
+ } {
+ \def\currentSETUPvariant{:\xmlatt{#1}{variant}}
+ }
+ \edef\currentSETUPfullname {
+ \currentSETUPprefix
+ \currentSETUPname
+ \currentSETUPvariant
+ \currentSETUPgenerated
+ }
+\stopxmlsetups
+
+\startxmlsetups xml:setups:register
+ \xmlsetup{#1}{xml:setups:assemblename}
+ % not really needed if we just use setups
+ \expanded{\texcommand[stp:x:\currentSETUPfullname]{#1}}
+\stopxmlsetups
+
+\startxmlsetups xml:setups:basics
+ \xmlinclude{#1}{include}{filename}%
+ \xmlsetsetup {#1} {
+ sequence|string|variable|assignments|keywords|content|displaymath|index|math|
+ nothing|file|position|reference|csname|destination|triplet|word|
+ resolve|parameter|constant|inherit|parameter|define
+ } {xml:setups:*}
+\stopxmlsetups
+
+\xmlregisterdocumentsetup{setups}{xml:setups:basics}
+
+\unexpanded\def\loadsetups{\complexorsimple\loadsetups}
+
+\let\loadedsetups\empty % we load more setups, setups:<name>
+
+\def\simpleloadsetups
+ {\doifnotmode{no-setup-main}{\complexloadsetups[cont-en.xml]}}
+
+\def\complexloadsetups[#1]%
+ {\doifsomething{#1}
+ {\doonlyonce{setups:#1}
+ {\doglobal\prependtocommalist{setups:#1}\loadedsetups % last overloads first
+ \xmlloadonly{setups:#1}{#1}{setups}%
+ \xmlfilter{setups:#1}{/interface/command/command(xml:setups:register)}}}} % qualified path saves > 50% runtime
+
+\newif\ifshortsetup
+
+\unexpanded\def\setup {\shortsetupfalse\doshowsetup}
+\unexpanded\def\showsetup {\shortsetupfalse\doshowsetup}
+\unexpanded\def\shortsetup{\shortsetuptrue \doshowsetup}
+\unexpanded\def\setupsetup{\dodoubleargument\getparameters[\??stp]}
+
+%unexpanded\def\showsetupinlist#1#2#3{\shortsetupfalse\showsetupindeed{#3}\par}
+\unexpanded\def\showsetupinlist#1#2#3{\shortsetupfalse\xmlsetup{#3}{xml:setups:typeset}\par}
+
+% todo: only references in lists
+
+\def\doshowsetup
+ {\dosingleempty\dodoshowsetup}
+
+\def\dodoshowsetup[#1]%
+ {\iffirstargument
+ \dododoshowsetup{#1}%
+ \else
+ \expandafter\dododoshowsetup
+ \fi}
+
+\def\dododoshowsetup#1% this will trigger 'used'
+ {\registersort[texcommand][stp:x:#1]%
+ \showsetupindeed{#1}}
+
+% \def\showsetupindeed#1%
+% {\xmlfilterlist{\loadedsetups}{interface/command[@name='#1']/command(xml:setups:typeset)}}
+
+% \def\showsetupindeed#1%
+% {\xmlfilterlist{\loadedsetups}{/interface/command['#1' == (@type=='environment' and 'start' or '') .. @name]/command(xml:setups:typeset)}}
+
+% \setelementnature[setup][display]
+% \setelementnature[setup][mixed]
+
+\def\showsetupindeed#1%
+ {\startelement[setup][name=#1]%
+ \startelement[noexport][comment={setup definition #1}]
+ \xmlfilterlist{\loadedsetups}{/interface/command['#1' == (@type=='environment' and '\e!start' or '') .. @name]/command(xml:setups:typeset)}%
+ \stopelement
+ \stopelement}
+
+\unexpanded\def\placesetup {\placelistofsorts[texcommand][\c!criterium=\v!used]}
+\unexpanded\def\placeallsetups{\placelistofsorts[texcommand][\c!criterium=\v!all ]}
+
+\let\placeeverysetup\placeallsetups
+
+%D Typesetting:
+
+\setupxml
+ [%\c!method=mkiv, % mixed mode
+ \c!default=\v!hidden, % ignore elements that are not defined
+ \c!compress=\v!yes, % strip comment
+ \c!entities=\v!yes] % replace entities
+
+\newcounter\currentSETUPargument
+\newcounter\maximumSETUPargument
+
+\def\currentSETUPwidth{0pt}
+
+\startxmlsetups xml:setups:typeset
+ \getvalue{\e!start setuptext}
+ \tttf
+ \nohyphens
+ \veryraggedright
+ \doglobal\newcounter\currentSETUPargument
+ \xdef\maximumSETUPargument{\xmlcount{#1}{/arguments/*}}
+ \edef\currentSETUPhash{\xmlatt{#1}{hash}}
+ \bgroup
+ \enablemode[setups-pass-one]%
+ \doif {\xmlatt{#1}{generated}} {yes} {
+ \ttsl
+ }
+ \letterbackslash
+ \doif {\xmlatt{#1}{type}} {environment} {
+ \e!start
+ }
+ \xmlfilter{#1}{/sequence/first()}
+ \ignorespaces
+ \egroup
+ \ifshortsetup
+ % nothing
+ \else
+ \xmldoif{#1}{/arguments} {
+ \bgroup
+ \enablemode[setups-pass-one]
+ \doglobal\newcounter\currentSETUPargument
+ \ignorespaces
+ \xmlfilter{#1}{/arguments/text()}
+ \egroup
+ }
+ \doif {\xmlatt{#1}{type}} {environment} {
+ \bgroup
+ \enablemode[setups-pass-one]%
+ \hskip.5em\unknown\hskip.5em
+ \doif {\xmlatt{#1}{generated}} {yes} {
+ \ttsl
+ }
+ \tex{\e!stop}
+ \xmlfilter{#1}{/sequence/first()}
+ \ignorespaces
+ \egroup
+ }
+ \endgraf
+ \xmldoif{#1}{/arguments} {
+ \bgroup
+ \enablemode[setups-pass-two]
+ \doglobal\newcounter\currentSETUPargument
+ %\blank[\v!line] % packed mode (we could do \startunpacked ...)
+ \godown[.75\lineheight]
+ \switchtobodyfont[\v!small]
+ \ignorespaces\xmlfilter{#1}{/arguments/text()}\endgraf
+ \egroup
+ }
+ \fi
+ \getvalue{\e!stop setuptext}
+\stopxmlsetups
+
+\setupsetup
+ [\c!before=,
+ \c!after=,
+ \c!command=\setup,
+ \c!criterium=\v!used]
+
+\startxmlsetups xml:setups:resolve
+ \ignorespaces
+ \xmlfilterlist{\loadedsetups}{/interface/define[@name='\xmlatt{#1}{name}']/first()}
+\stopxmlsetups
+
+%D This is the first pass; here we generate the top line.
+
+\startxmlsetups xml:setups:define
+ \ignorespaces\xmlflush{#1}
+\stopxmlsetups
+
+\startxmlsetups xml:setups:sequence
+ \ignorespaces\xmlflush{#1}
+\stopxmlsetups
+
+\startxmlsetups xml:setups:string
+ \xmlatt{#1}{value}\ignorespaces
+\stopxmlsetups
+
+\startxmlsetups xml:setups:content \showSETUPcomponent{#1}{content} {content} \stopxmlsetups
+\startxmlsetups xml:setups:displaymath \showSETUPcomponent{#1}{displaymath}{display math}\stopxmlsetups
+\startxmlsetups xml:setups:index \showSETUPcomponent{#1}{index} {index} \stopxmlsetups
+\startxmlsetups xml:setups:math \showSETUPcomponent{#1}{math} {math} \stopxmlsetups
+\startxmlsetups xml:setups:nothing \showSETUPcomponent{#1}{nothing} {nothing} \stopxmlsetups
+\startxmlsetups xml:setups:file \showSETUPcomponent{#1}{file} {file name} \stopxmlsetups
+\startxmlsetups xml:setups:position \showSETUPcomponent{#1}{position} {position} \stopxmlsetups
+\startxmlsetups xml:setups:reference \showSETUPcomponent{#1}{reference} {reference} \stopxmlsetups
+\startxmlsetups xml:setups:csname \showSETUPcomponent{#1}{csname} {csname} \stopxmlsetups
+\startxmlsetups xml:setups:destination \showSETUPcomponent{#1}{destination}{destination} \stopxmlsetups
+\startxmlsetups xml:setups:triplet \showSETUPcomponent{#1}{triplet} {triplet} \stopxmlsetups
+\startxmlsetups xml:setups:word \showSETUPcomponent{#1}{word} {word} \stopxmlsetups
+
+\def\showSETUPcomponent#1#2#3%
+ {\doifelsemode{setups-pass-one}
+ {\getvalue{showSETUP#2}{#1}}
+ {\simpleSETUPargument{#3}}}
+
+%D This is the second pass; here we generate the table.
+
+\unexpanded\def\startfirstSETUPcolumn#1%
+ {\bgroup
+ \advance\leftskip 2em
+ \noindent\llap{\hbox to 2em{#1\hss}}}
+
+\unexpanded\def\stopfirstSETUPcolumn
+ {\endgraf
+ \egroup}
+
+\unexpanded\def\startsecondSETUPcolumn#1#2%
+ {\bgroup
+ \advance\hangindent\dimexpr\currentSETUPwidth+2.5em\relax
+ \noindent \hbox to \hangindent{#1\hss\hbox to 2.5em{\hss#2\hss}}}
+
+\unexpanded\def\stopsecondSETUPcolumn
+ {\endgraf
+ \egroup}
+
+\def\secondSETUPcolumn#1#2%
+ {\startsecondSETUPcolumn{#1}{#2}\stopsecondSETUPcolumn}
+
+\def\previousSETUPargument{\currentSETUPargument}
+
+\startxmlsetups xml:setups:parameter:measure
+ \setbox0=\hbox{\c!setup!reserved!{\xmlatt{#1}{name}}}
+ \ifdim\wd0>\currentSETUPwidth\xdef\currentSETUPwidth{\the\wd0}\fi
+\stopxmlsetups
+
+\startxmlsetups xml:setups:assignments
+ \doifelsemode{setups-pass-one} {
+ \showSETUPassignment{#1}
+ } {
+ \xdef\currentSETUPwidth{0pt}%
+ \bgroup
+ \xmlfilter{#1}{/parameter/command(xml:setups:parameter:measure)}
+ \egroup
+ \startfirstSETUPcolumn{\showSETUPnumber}%
+ \ignorespaces
+ \xmldoifelse{#1}{/(parameter|inherit)}{
+ \xmlflush{#1}
+ } {
+ ...
+ }
+ \let\previousSETUPargument\currentSETUPargument
+ \stopfirstSETUPcolumn
+ \blank[\v!halfline]
+ \ignorespaces
+ }
+\stopxmlsetups
+
+\startxmlsetups xml:setups:keywords
+ \doifelsemode{setups-pass-one} {
+ \showSETUPkeyword{#1}
+ } {
+ \startfirstSETUPcolumn{\showSETUPnumber}%
+ \ignorespaces
+ \xmlflush{#1}
+ \let\previousSETUPargument\currentSETUPargument
+ \stopfirstSETUPcolumn
+ \blank[\v!halfline]
+ \ignorespaces
+ }
+\stopxmlsetups
+
+\startxmlsetups xml:setups:parameter
+ \startsecondSETUPcolumn{\c!setup!reserved!{\xmlatt{#1}{name}}}{=}
+ \ignorespaces
+ \xmlflush{#1}
+ \doifmode{interface:setup:defaults} {
+ \ifx\currentSETUPhash\empty \else
+ \begingroup
+ % todo, make a one level expansion of parameter
+ \let\emwidth \relax
+ \let\exheight\relax
+ \edef\currentSETUPvalue{\csname named\currentSETUPhash parameter\endcsname\empty{\xmlatt{#1}{name}}}
+ \ifx\currentSETUPvalue\empty \else
+ =\space
+ \detokenize\expandafter{\currentSETUPvalue}
+ \fi
+ \endgroup
+ \fi
+ }
+ \stopsecondSETUPcolumn
+ \ignorespaces
+\stopxmlsetups
+
+\startxmlsetups xml:setups:constant
+ \doifelsemode {setups-pass-one} {
+ } {
+ \doif {\xmlatt{#1}{default}} {yes} {
+ \underbar % next needs to be {braced}
+ }
+ {\c!setup!reserved!{\xmlatt{#1}{type}}}
+ \space
+ \ignorespaces
+ }
+\stopxmlsetups
+
+\startxmlsetups xml:setups:variable
+ \doifelsemode {setups-pass-one} {
+ \expanded{\setupintfont{\xmlatt{#1}{value}}}\ignorespaces
+ } {
+ \c!setup!reserved!{\xmlatt{#1}{value}}
+ \space
+ \ignorespaces
+ }
+\stopxmlsetups
+
+\startxmlsetups xml:setups:inherit
+ \secondSETUPcolumn {
+ \c!setup!text!{\getmessage{setup}{inherits}}
+ \enspace
+ \letterbackslash
+ \xmlatt{#1}{name}
+ } {}
+ \ignorespaces
+\stopxmlsetups
+
+\def\simpleSETUPargument#1%
+ {\startfirstSETUPcolumn{\showSETUPnumber}%
+ \c!setup!internal!{#1}%
+ \stopfirstSETUPcolumn
+ \blank[\v!halfline]
+ \ignorespaces}
+
+\c!setup!definereserved {cd:command} {\c!setup!internal!{\getmessage{setup}{command}}}
+\c!setup!definereserved {cd:dimension} {\c!setup!internal!{\getmessage{setup}{dimension}}}
+\c!setup!definereserved {cd:file} {\c!setup!internal!{\getmessage{setup}{file}}}
+\c!setup!definereserved {cd:name} {\c!setup!internal!{\getmessage{setup}{identifier}}}
+\c!setup!definereserved {cd:character} {\c!setup!internal!{\getmessage{setup}{character}}}
+\c!setup!definereserved {cd:mark} {\c!setup!internal!{\getmessage{setup}{mark}}}
+\c!setup!definereserved {cd:number} {\c!setup!internal!{\getmessage{setup}{number}}}
+\c!setup!definereserved {cd:reference} {\c!setup!internal!{\getmessage{setup}{reference}}}
+\c!setup!definereserved {cd:plural} {\c!setup!internal!{\getmessage{setup}{plural}}}
+\c!setup!definereserved {cd:singular} {\c!setup!internal!{\getmessage{setup}{singular}}}
+\c!setup!definereserved {cd:text} {\c!setup!internal!{\getmessage{setup}{text}}}
+\c!setup!definereserved {cd:formula} {\c!setup!internal!{\getmessage{setup}{formula}}}
+\c!setup!definereserved {cd:file} {\c!setup!internal!{\getmessage{setup}{file}}}
+\c!setup!definereserved {cd:matrix} {\c!setup!internal!{\getmessage{setup}{matrix}}}
+\c!setup!definereserved {cd:list} {\c!setup!internal!{\getmessage{setup}{list}}}
+\c!setup!definereserved {cd:section} {\c!setup!internal!{\getmessage{setup}{section}}}
+
+\c!setup!definereserved {cd:noargument} {\c!setup!command! {}}
+\c!setup!definereserved {cd:oneargument} {\c!setup!command! {\#1}}
+\c!setup!definereserved {cd:twoarguments} {\c!setup!command! {\#1\#2}}
+\c!setup!definereserved {cd:threearguments} {\c!setup!command! {\#1\#2\#3}}
+
+%D Auxiliary.
+
+\unexpanded\def\showSETUP#1#2#3%
+ {\bgroup
+ \doglobal\increment\currentSETUPargument
+ \setbox0=\hbox
+ {\doifelse{\xmlatt{#1}{list}}{yes}{#3}{#2}}%
+ \setbox2=\hbox to \wd0
+ {\hss
+ \raise1ex\hbox
+ {\tx\ifcase\maximumSETUPargument\relax
+ \or*\else\currentSETUPargument
+ \fi}%
+ \hss}%
+ \setbox4=\hbox to \wd0
+ {\hss
+ \lower2ex\hbox
+ \bgroup
+ \txx\doif{\xmlatt{#1}{optional}}{yes}{\c!setup!internal!{\getmessage{setup}{optional}}}%
+ \egroup
+ \hss}%
+ \ht2\ht\strutbox
+ \dp4\dp\strutbox
+ \hskip.5em\hsmash{\box0}\hsmash{\box4}\box2%
+ \egroup
+ \ignorespaces}
+
+\def\showSETUPnumber
+ {\doglobal\increment\currentSETUPargument
+ \hbox to 2em
+ {\ifcase\maximumSETUPargument\relax
+ \or*\else\currentSETUPargument
+ \fi
+ \hss}}
+
+\def\showSETUPassignment #1{\showSETUP{#1}{[.\lower.5ex\hbox{=}.]} {[..,.\lower.5ex\hbox{=}.,..]}}
+\def\showSETUPkeyword #1{\showSETUP{#1}{[...]} {[...,...]}}
+\def\showSETUPargument #1{\showSETUP{#1}{\leftargument..\rightargument} {\leftargument..,...,..\rightargument}}
+\def\showSETUPdisplaymath#1{\showSETUP{#1}{\$\$...\$\$} {\$\$...\$\$}}
+\def\showSETUPindex #1{\showSETUP{#1}{\leftargument...\rightargument} {\leftargument..+...+..\rightargument}}
+\def\showSETUPmath #1{\showSETUP{#1}{\$...\$} {\$...\$}}
+\def\showSETUPnothing #1{\showSETUP{#1}{...} {}}
+\def\showSETUPfile #1{\showSETUP{#1}{~...~} {}}
+\def\showSETUPposition #1{\showSETUP{#1}{(...)} {(...,...)}}
+\def\showSETUPreference #1{\showSETUP{#1}{[...]} {[...,...]}}
+\def\showSETUPcsname #1{\showSETUP{#1}{{\c!setup!command!{}}} {}}
+\def\showSETUPdestination#1{\showSETUP{#1}{[\leftargument..[ref]\rightargument]}{[..,\leftargument..[ref,..]\rightargument,..]}}
+\def\showSETUPtriplet #1{\showSETUP{#1}{[x:y:z=]} {[x:y:z=,..]}}
+\def\showSETUPword #1{\showSETUP{#1}{\leftargument...\rightargument} {\leftargument.. ... ..\rightargument}}
+\def\showSETUPcontent #1{\showSETUP{#1}{\leftargument...\rightargument} {\leftargument.. ... ..\rightargument}}
+
+% A prelude to a rewrite and some more:
+
+% \definetype[parametercommand][\v!type]
+% \definetype[parameterkey] [\v!type]
+% \definetype[parametervalue] [\v!type][\c!space=\v!on]
+
+\definetype[parametercommand]
+\definetype[parameterkey]
+\definetype[parametervalue] [\c!space=\v!on]
+
+\setuptype [parametercommand] [\c!color=darkmagenta]
+\setuptype [parametervalue] [\c!color=darkyellow]
+
+\startxmlsetups xml:setups:parameters:value
+ \edef\currentsetupparameterkey {\xmlatt{#1}{name}}
+ \edef\currentsetupparametervalue{\csname named\currentsetupparametercategory parameter\endcsname\currentsetupparameterinstance\currentsetupparameterkey}
+ \ifx\currentsetupparameterinstance\empty
+ \expanded {
+ \NC \parameterkey {\currentsetupparameterkey}
+ \NC \parametervalue{\detokenize\expandafter{\currentsetupparametervalue}}
+ \NC \NR
+ }
+ \else\ifx\currentsetupparametervalue\empty
+ \else
+ \edef\currentsetupparameterdefault{\csname named\currentsetupparametercategory parameter\endcsname\empty\currentsetupparameterkey}
+ \ifx\currentsetupparametervalue\currentsetupparameterdefault
+ % skip
+ \else
+ \expanded {
+ \NC \parameterkey {\currentsetupparameterkey}
+ \NC \parametervalue{\detokenize\expandafter{\currentsetupparametervalue}}
+ \NC \NR
+ }
+ \fi
+ \fi\fi
+\stopxmlsetups
+
+\startxmlsetups xml:setups:parameters:values
+ \blank[big]
+ \expanded {
+ \parametercommand {
+ \currentsetupparametercommand
+ \space:\space
+ \ifx\currentsetupparameterinstance\empty
+ defaults
+ \else
+ \currentsetupparameterinstance
+ \fi
+ }
+ }
+ \blank[big,samepage]
+ \starttabulate[|l|p|]
+ \xmlall
+ {#1}
+ {/interface/command[@name=='\currentsetupparametercommand']/arguments/assignments/parameter/command(xml:setups:parameters:value)}
+ \ifnum\noftabulaterows = \zerocount
+ \NC \parameterkey{no specific settings} \NC \NC \NR
+ \fi
+ \stoptabulate
+\stopxmlsetups
+
+\starttexdefinition showrootvalues [#1]
+ \edef\currentsetupparametercategory{#1}
+ \edef\currentsetupparametercommand{setup#1}
+ \let\currentsetupparameterinstance\empty
+ \xmlsetup{\loadedsetups}{xml:setups:parameters:values}
+\stoptexdefinition
+
+\starttexdefinition showinstancevalues [#1]#2[#3]
+ \edef\currentsetupparametercategory{#1}
+ \edef\currentsetupparametercommand{setup#1}
+ \edef\currentsetupparameterinstance{#3}
+ \xmlsetup{\loadedsetups}{xml:setups:parameters:values}
+\stoptexdefinition
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/x-set-12.mkiv b/tex/context/modules/mkiv/x-set-12.mkiv
new file mode 100644
index 000000000..c60445313
--- /dev/null
+++ b/tex/context/modules/mkiv/x-set-12.mkiv
@@ -0,0 +1,267 @@
+%D \module
+%D [ file=x-set-12,
+%D version=2004.10.31,
+%D remark=setupx.tex: 1998.07.20 and later,
+%D title=\CONTEXT\ Setup Definitions,
+%D subtitle=Macro Definitions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% included loading overhead
+%
+% 2.55 / 2.40 (luatex)
+% 1.90 / 1.80 (luajittex)
+
+% \newif\ifcachedcommand
+% \newif\ifcalledcommand
+%
+% \cachedcommandtrue
+% \calledcommandtrue
+%
+% \usemodule[speedtest]
+
+\usemodule[set-11]
+
+\unprotect
+
+% \starttext
+% \setup{installlanguage}
+% \placesetup
+% \stoptext
+
+\definecolor[TitleColor][r=.375,g=.125,b=.125]
+\definecolor[TitleColor][r=.125,g=.375,b=.125]
+\definecolor[TitleColor][r=.125,g=.125,b=.375]
+\definecolor[TitleColor][r=.375,g=.375,b=.125]
+\definecolor[TitleColor][r=.375,g=.125,b=.375]
+\definecolor[TitleColor][r=.125,g=.375,b=.375]
+
+\definecolor[TitleColor][r=.25,g=.20,b=.15]
+\definecolor[TitleColor][r=.25,g=.15,b=.20]
+\definecolor[TitleColor][r=.20,g=.15,b=.25]
+\definecolor[TitleColor][r=.20,g=.25,b=.15]
+\definecolor[TitleColor][r=.15,g=.20,b=.25]
+\definecolor[TitleColor][r=.15,g=.25,b=.20]
+
+\startinterface english \loadsetups[cont-en.xml] \stopinterface
+\startinterface dutch \loadsetups[cont-nl.xml] \stopinterface
+\startinterface german \loadsetups[cont-de.xml] \stopinterface
+\startinterface french \loadsetups[cont-fr.xml] \stopinterface
+\startinterface italian \loadsetups[cont-it.xml] \stopinterface
+\startinterface czech \loadsetups[cont-cs.xml] \stopinterface
+\startinterface romanian \loadsetups[cont-ro.xml] \stopinterface
+
+\startinterface dutch \definecolor[LocalColor][r=.75,g=.25,b=.25] \stopinterface
+\startinterface english \definecolor[LocalColor][r=.25,g=.75,b=.25] \stopinterface
+\startinterface german \definecolor[LocalColor][r=.25,g=.25,b=.75] \stopinterface
+\startinterface french \definecolor[LocalColor][r=.75,g=.75,b=.25] \stopinterface
+\startinterface czech \definecolor[LocalColor][r=.75,g=.25,b=.75] \stopinterface
+\startinterface italian \definecolor[LocalColor][r=.25,g=.75,b=.75] \stopinterface
+\startinterface romanian \definecolor[LocalColor][r=.5,g=.4,b=.3] \stopinterface
+% \definecolor[LocalColor][r=.5,g=.3,b=.4]
+% \definecolor[LocalColor][r=.4,g=.3,b=.5]
+% \definecolor[LocalColor][r=.4,g=.5,b=.3]
+% \definecolor[LocalColor][r=.3,g=.4,b=.5]
+% \definecolor[LocalColor][r=.3,g=.5,b=.4]
+
+\startinterface english \definecolor[TitleColor][r=.375,g=.125,b=.125] \stopinterface
+\startinterface dutch \definecolor[TitleColor][r=.125,g=.375,b=.125] \stopinterface
+\startinterface german \definecolor[TitleColor][r=.125,g=.125,b=.375] \stopinterface
+\startinterface french \definecolor[TitleColor][r=.375,g=.375,b=.125] \stopinterface
+\startinterface italian \definecolor[TitleColor][r=.375,g=.125,b=.375] \stopinterface
+\startinterface czech \definecolor[TitleColor][r=.125,g=.375,b=.375] \stopinterface
+\startinterface romanian \definecolor[TitleColor][r=.25,g=.20,b=.15] \stopinterface
+% \definecolor[TitleColor][r=.25,g=.15,b=.20]
+% \definecolor[TitleColor][r=.20,g=.15,b=.25]
+% \definecolor[TitleColor][r=.20,g=.25,b=.15]
+% \definecolor[TitleColor][r=.15,g=.20,b=.25]
+% \definecolor[TitleColor][r=.15,g=.25,b=.20]
+
+\definecolor [lightgray] [s=.9]
+\definecolor [darkgray] [s=.1]
+
+\usetypescript[palatino]
+\setupbodyfont[palatino,9pt]
+
+\defineoverlay
+ [cover]
+ [\hbox to \paperwidth{\hss\reuseMPgraphic{cover+back}}]
+
+\defineoverlay
+ [back]
+ [\hbox to \paperwidth{\reuseMPgraphic{cover+back}\hss}]
+
+\startreusableMPgraphic{cover+back}
+ numeric h, w ; path p, q, r ; color f, d ; pair s ;
+ h := OverlayHeight ; w := 2*OverlayWidth ;
+ r := unitsquare xyscaled (w,h) ;
+ fill r withcolor \MPcolor{lightgray} ;
+ set_grid(w,h,w/8,w/16) ;
+ forever :
+ s := center r randomized (w,h) ;
+ if new_on_grid(xpart s, ypart s) :
+ s := (dx,dy) ;
+ p := fullsquare xyscaled(w/4,w/8) ;
+ q := (-4w,ypart ulcorner p) -- .5[ulcorner p, urcorner p] -- (4w,ypart urcorner p) ;
+ q := q shifted (0,-w/24) ;
+ p := p randomized (w/40,w/40) ;
+ q := q randomized (0,w/100) ;
+ q := q cutafter (p cutafter point 3 of p) ;
+ q := q cutbefore (p cutbefore point 3 of p) ;
+ d := .5[\MPcolor{LocalColor},\MPcolor{lightgray}] randomized (.5,.9) ;
+ f := \MPcolor{lightgray} randomized (.5,.9) ;
+ pickup pencircle scaled (w/100) ;
+ fill p shifted s withcolor f ;
+ draw p shifted s withcolor d ;
+ draw q shifted s withcolor d ;
+ fi ;
+ exitif grid_full ;
+ endfor ;
+ setbounds currentpicture to r ;
+\stopreusableMPgraphic
+
+\definelayout
+ [titlepage]
+ [\c!backspace=1cm,
+ \c!topspace=1cm,
+ \c!width=\v!middle,
+ \c!height=\v!middle,
+ \c!header=0pt,
+ \c!footer=0pt]
+
+\setuplayout
+ [\c!backspace=2cm,
+ \c!topspace=1.5cm,
+ \c!header=0cm,
+ \c!footer=0cm,
+ \c!width=\v!middle,
+ \c!height=\v!middle]
+
+\setuppagenumbering
+ [\c!alternative=\v!doublesided]
+
+\setupsetup
+ [\c!criterium=\v!all]
+
+\setupframedtexts
+ [setuptext]
+ [\c!before=\blank,
+ \c!after=\blank,
+ \c!frame=\v!on,
+ \c!rulethickness=1pt,
+ \c!framecolor=TitleColor]
+
+\setupunderbar
+ [\c!rulethickness=1pt,
+ \c!rulecolor=TitleColor]
+
+\starttext
+
+\setupbackgrounds
+ [\v!rightpage]
+ [\c!background=cover]
+
+\setuplayout
+ [titlepage]
+
+\startsetups text:commands
+ \startinterface dutch \strut commando's \par \stopinterface
+ \startinterface english \strut commands \par \stopinterface
+ \startinterface german \strut befehle \par \stopinterface
+ \startinterface french \strut commandes \par \stopinterface
+ \startinterface czech \strut p\v{r}ikazy \par \stopinterface
+ \startinterface italian \strut comandi \par \stopinterface
+ \startinterface romanian \strut comenzile \par \stopinterface
+\stopsetups
+
+\startsetups text:uppercase
+ \startinterface dutch NL\stopinterface
+ \startinterface english EN\stopinterface
+ \startinterface german DE\stopinterface
+ \startinterface french FR\stopinterface
+ \startinterface czech CS\stopinterface
+ \startinterface italian IT\stopinterface
+ \startinterface romanian RO\stopinterface
+\stopsetups
+
+\startsetups text:lowercase
+ \startinterface dutch \strut nl / nederlands \par \stopinterface
+ \startinterface english \strut en / english \par \stopinterface
+ \startinterface german \strut de / deutsch \par \stopinterface
+ \startinterface french \strut fr / fran\c{c}ais \par \stopinterface
+ \startinterface czech \strut cs / \v{c}esk\'y \par \stopinterface
+ \startinterface italian \strut it / italiano \par \stopinterface
+ \startinterface romanian \strut ro / rom\^{a}n\u{a} \par \stopinterface
+\stopsetups
+
+\startmakeup[\v!standard]
+ \dontcomplain
+ \setupalign[\v!left]
+ \startcolor[TitleColor]
+ \definedfont[RegularBold at 100pt]\setstrut
+ \strut Con\TeX t \par
+ \definedfont[RegularBold at 50pt]\setstrut
+ \setups[text:commands]
+ \vfill
+ \definedfont[RegularBold at 150pt]\setstrut
+ \setups[text:uppercase]
+ \stopcolor
+\stopmakeup
+
+\setuplayout % needed ?
+
+\setupbackgrounds
+ [\v!rightpage]
+ [\c!background=]
+
+\startmakeup[\v!standard]
+ \dontcomplain
+ \startcolor[TitleColor]
+ \definedfont[RegularBold at 100pt]\setstrut
+ \setupalign[\v!left]
+ \strut Con\TeX t \par
+ \definedfont[RegularBold at 50pt]\setstrut
+ \setups[text:commands]
+ \vfill
+ \definedfont[RegularBold at 24pt]\setupinterlinespace
+ \setups[text:lowercase]
+ \par \strut \currentdate \par
+ \stopcolor
+\stopmakeup
+
+\protect
+
+\placeeverysetup
+
+\unprotect
+
+\page[\v!yes,\v!blank,\v!right,\v!left]
+
+\setuplayout
+ [titlepage]
+
+\setupbackgrounds
+ [\v!leftpage]
+ [\c!background=back]
+
+\startmakeup[\v!standard][\c!page=]
+ \dontcomplain
+ \startcolor[TitleColor]
+ \definedfont[RegularBold at 24pt]\setupinterlinespace
+ \setupalign[\v!left]
+ \vfill
+ PRAGMA ADE \par
+ Ridderstraat 27 \par
+ 8061GH Hasselt NL \par
+ www.pragma-ade.com \par
+ \stopcolor
+\stopmakeup
+
+\protect
+
+\stoptext
diff --git a/tex/context/modules/mkiv/x-steps.mkiv b/tex/context/modules/mkiv/x-steps.mkiv
new file mode 100644
index 000000000..29b3f7eaa
--- /dev/null
+++ b/tex/context/modules/mkiv/x-steps.mkiv
@@ -0,0 +1,102 @@
+%D \module
+%D [ file=m-steps,
+%D version=2001.05.28,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Step Charts \& Tables,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D The \XML\ interface. This module can be made way more efficient
+%D in \MKIV\ using textext but it makes only sense to do this when
+%D I really need it in a demanding application. Probably half of the
+%D code in m-steps.tex can go.
+
+\usemodule[m][steps]
+
+\unprotect
+
+\installcorenamespace {xmlstepchart}
+\installcorenamespace {xmlsteptable}
+
+\def\xmlstepchartdirective#1{\executeifdefined{\??xmlstepchart#1}\gobbletwoarguments} % {#2}{#3}
+\def\xmlsteptabledirective#1{\executeifdefined{\??xmlsteptable#1}\gobbletwoarguments} % {#2}{#3}
+
+\setvalue{\??xmlstepchart charts}{\setsomevalue\@@STPC}
+\setvalue{\??xmlstepchart cells}{\setsomevalue\@@STEC}
+\setvalue{\??xmlstepchart texts}{\setsomevalue\@@STET}
+\setvalue{\??xmlstepchart lines}{\setsomevalue\@@STEL}
+
+\setvalue{\??xmlsteptable tables}{\setsomevalue\@@STPT}
+\setvalue{\??xmlsteptable cells}{\setsomevalue\@@STEC}
+\setvalue{\??xmlsteptable texts}{\setsomevalue\@@STET}
+\setvalue{\??xmlsteptable lines}{\setsomevalue\@@STEL}
+
+\startxmlsetups xml:ct:define
+ \xmlsetsetup {#1} {ct:*} {xml:ct:*}
+ % \xmlsetsetup {#1} {ct:stepaligntable/cells} {xml:ct:stepaligntable:cells}
+ % \xmlsetsetup {#1} {ct:stepaligntable/lines} {xml:ct:stepaligntable:lines}
+\stopxmlsetups
+
+\xmlregisterns{ct}{stepcharts}
+
+\xmlregistersetup{xml:ct:define}
+
+\startxmlsetups xml:ct:prep
+ \expanded{\prep[\xmltoparameters{#1}]}{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups xml:ct:text
+ \expanded{\text[\xmltoparameters{#1}]}{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups xml:ct:texts
+ \expanded{\texts[\xmltoparameters{#1}]}{\xmltext{#1}{/top}} {\xmltext{#1}{/bot}}
+\stopxmlsetups
+
+\startxmlsetups xml:ct:cell
+ \expanded{\cell[\xmltoparameters{#1}]}{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups xml:ct:cells
+ \expanded{\cells[\xmltoparameters{#1}]}{\xmltext{#1}{/top}} {\xmltext{#1}{/bot}}
+\stopxmlsetups
+
+\startxmlsetups xml:ct:lines
+ \expanded{\startlines[\xmltoparameters{#1}]}
+ \xmlflush{#1}
+ \stoplines
+\stopxmlsetups
+
+\startxmlsetups xml:ct:steptable
+ \expanded{\startSTEPtable[\xmltoparameters{#1}]}
+ \xmlflush{#1}
+ \stopSTEPtable
+\stopxmlsetups
+
+\startxmlsetups xml:ct:stepchart
+ \expanded{\startSTEPchart[\xmltoparameters{#1}]}
+ \xmlflush{#1}
+ \stopSTEPchart
+\stopxmlsetups
+
+% \startxmlsetups xml:ct:stepaligntable
+% \expanded{\startSTEPaligntable[\xmltoparameters{#1}]}
+% \xmlflush{#1}
+% \stopSTEPaligntable
+% \stopxmlsetups
+%
+% \startxmlsetups xml:ct:stepaligntable:cells
+% \expanded{\cells[\xmltoparameters{#1}]} {\xmltext{#1}{/ct:c1}} {\xmltext{#1}{/ct:c2}} {\xmltext{#1}{/ct:c3}}
+% \stopxmlsetups
+%
+% \startxmlsetups xml:ct:stepaligntable:lines
+% \expanded{\setupSTEPlines[\xmltoparameters{#1}]}
+% \xmlflush{#1}
+% \stopxmlsetups
+
+\protect \endinput
diff --git a/tex/context/modules/mkiv/x-udhr.mkiv b/tex/context/modules/mkiv/x-udhr.mkiv
new file mode 100644
index 000000000..e081bfd59
--- /dev/null
+++ b/tex/context/modules/mkiv/x-udhr.mkiv
@@ -0,0 +1,98 @@
+%D \module
+%D [ file=x-udhr,
+%D version=2011.06.11,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Unicode Language Test Files,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D The XML files can be fetched from: \from[http://unicode.org/udhr/].
+
+\startmodule[udhr]
+
+\startxmlsetups udhr:basics
+ \xmlsetsetup {#1} {*} {udhr:*}
+\stopxmlsetups
+
+\xmlregistersetup{udhr:basics}
+
+\startxmlsetups udhr:udhr
+
+ \mainlanguage[\xmlatt{#1}{language}]
+
+ \starttitle[title=\xmltext{#1}{/title}]
+ \xmlfirst{#1}{/preamble}
+ \xmlall{#1}{/article}
+ \stoptitle
+
+\stopxmlsetups
+
+\startxmlsetups udhr:preamble
+ \startsubject[title=\xmltext{#1}{/title}]
+ \xmlall{#1}{/*)}
+ \stopsubject
+\stopxmlsetups
+
+\startxmlsetups udhr:article
+ \startsubject[title=\xmltext{#1}{/title}]
+ \xmlall{#1}{/*)}
+ \stopsubject
+\stopxmlsetups
+
+\startxmlsetups udhr:orderedlist
+ \startitemize[n]
+ \xmlflush{#1}
+ \stopitemize
+\stopxmlsetups
+
+\startxmlsetups udhr:listitem
+ \startitem
+ \xmlflush{#1}
+ \stopitem
+\stopxmlsetups
+
+\startxmlsetups udhr:para
+ \xmlflush{#1}
+ \par
+\stopxmlsetups
+
+\setupbodyfont
+ [dejavu,10pt]
+
+\setuplayout
+ [width-=middle,
+ height=middle,
+ footer=0cm,
+ header=1.5cm]
+
+\setupwhitespace
+ [big]
+
+\setuphead
+ [chapter]
+ [header=high,
+ style=\bfb,
+ align=middle]
+
+\setuphead
+ [section]
+ [style=\bfa,
+ align=middle]
+
+\setuptolerance
+ [verytolerant]
+
+\continueifinputfile{x-udhr.mkiv}
+
+% todo: when argument given then process it
+
+\starttext
+ \xmlprocessfile{main}{udhr_nld.xml}{}
+\stoptext
+
+\stopmodule
diff --git a/tex/context/modules/mkiv/x-xfdf.mkiv b/tex/context/modules/mkiv/x-xfdf.mkiv
new file mode 100644
index 000000000..460220ed9
--- /dev/null
+++ b/tex/context/modules/mkiv/x-xfdf.mkiv
@@ -0,0 +1,72 @@
+%D \module
+%D [ file=x-xfdf,
+%D version=2011.09.07,
+%D title=\CONTEXT\ XML Modules,
+%D subtitle=\XFDF,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is a revival of using \XFDF, but now in a more \MKIV-ish way. We
+%D supported it long ago already in \MKII\ but never used it at a large
+%D scale (not that much user interest anyway).
+
+\startmodule[xfdf]
+
+% see xfdf-001.xfdf and xfdf-001.tex
+
+% %D Possible speedup but hardly worth the trouble.
+%
+% \startluacode
+%
+% local hashes = { } table.setmetatableindex(hashes,function(t,k) local v = { } t[k] = v return v end)
+%
+% function xml.functions.xfdf_collect_values(root)
+% local hash = hashes[root]
+% for c in xml.collected(root,"/xfdf/fields/field/value") do
+% hash[xml.parent(c).at.name] = c
+% end
+% end
+%
+% function xml.functions.xfdf_get_values(root,name)
+% return hashes[root][name]
+% end
+%
+% function lxml.xfdf_get_values(root,name)
+% xml.sprint(hashes[lxml.id(root)][name])
+% end
+%
+% \stopluacode
+%
+% \def\xfdfvalue#1#2%
+% {\ctxlua{lxml.xfdf_get_values("#1","#2")}}
+
+\startxmlsetups xfdf:define
+ \xmlsetsetup{#1}{*}{xfdf:*}
+ % \xmlfilter {#1}{./function(xfdf_collect_values)}
+\stopxmlsetups
+
+\xmlregisterns{xfdf}{http://ns.adobe.com/xfdf/}
+
+\xmlregisterdocumentsetup{xfdf}{xfdf:define}
+
+\startxmlsetups xfdf:value
+ \xmlflush{#1}
+\stopxmlsetups
+
+\def\xfdfload #1#2{\xmlloadonly{#1}{#2}{xfdf}}
+\def\xfdfvalue#1#2{\xmlfirst{#1}{/xfdf/fields/field[@name='#2']/value}}
+\def\xfdftext #1#2{\xmlfirst{#1}{/xfdf/fields/field[@name='#2']/value/paragraphs()}}
+
+% \startxmlsetups xfdf:b
+% \bold{\xmlflush{#1}}
+% \stopxmlsetups
+
+% \xfdfload {whatever}{xfdf-001.xfdf}
+% \xfdfvalue{whatever}{somefield}
+
+\stopmodule