summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Hagen <pragma@wxs.nl>2019-09-27 20:24:34 +0200
committerContext Git Mirror Bot <phg@phi-gamma.net>2019-09-27 20:24:34 +0200
commit0a5f59a9aa25b3de7e9659b39ad201aaf7eb5a67 (patch)
treea652bb083d6bc6a9b6309908dd5d09a539ac3859
parent58c7c9288160407c874930aac789ef6ef3faa6b5 (diff)
downloadcontext-0a5f59a9aa25b3de7e9659b39ad201aaf7eb5a67.tar.gz
2019-09-27 18:10:00
-rw-r--r--context/data/scite/context/lexers/data/scite-context-data-metafun.lua2
-rw-r--r--context/data/scite/context/lexers/data/scite-context-data-metapost.lua2
-rw-r--r--context/data/scite/context/scite-context-data-metafun.properties4
-rw-r--r--context/data/scite/context/scite-context-data-metapost.properties2
-rw-r--r--context/data/textadept/context/data/scite-context-data-metafun.lua2
-rw-r--r--context/data/textadept/context/data/scite-context-data-metapost.lua2
-rw-r--r--context/data/vscode/extensions/context/syntaxes/context-syntax-mps.json4
-rw-r--r--doc/context/documents/general/magazines/mag-1104-mkiv.pdfbin781988 -> 751131 bytes
-rw-r--r--doc/context/documents/general/manuals/lowlevel-boxes.pdfbin0 -> 75834 bytes
-rw-r--r--doc/context/documents/general/manuals/lowlevel-conditionals.pdfbin0 -> 95412 bytes
-rw-r--r--doc/context/documents/general/manuals/lowlevel-expansion.pdfbin0 -> 48778 bytes
-rw-r--r--doc/context/documents/general/manuals/lowlevel-registers.pdfbin0 -> 43435 bytes
-rw-r--r--doc/context/documents/general/manuals/luametafun.pdfbin0 -> 1514692 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-cs.pdfbin887551 -> 887655 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-de.pdfbin889628 -> 889580 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-en.pdfbin893466 -> 893435 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-fr.pdfbin885546 -> 885603 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-it.pdfbin891211 -> 891252 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-mapping-cs.pdfbin383751 -> 383751 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-mapping-de.pdfbin471626 -> 471430 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-mapping-en.pdfbin380397 -> 380397 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-mapping-fr.pdfbin384093 -> 384093 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-mapping-it.pdfbin383246 -> 383246 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-mapping-nl.pdfbin382226 -> 382225 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-mapping-ro.pdfbin620292 -> 620289 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-nl.pdfbin881171 -> 881208 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-ro.pdfbin885510 -> 885570 bytes
-rw-r--r--doc/context/presentations/context/2017/context-2017-css-selectors.pdfbin0 -> 130327 bytes
-rw-r--r--doc/context/presentations/context/2017/context-2017-css-selectors.tex238
-rw-r--r--doc/context/presentations/context/2017/context-2017-features-chaintest.pdfbin0 -> 15666 bytes
-rw-r--r--doc/context/presentations/context/2017/context-2017-features-chaintest.tex49
-rw-r--r--doc/context/presentations/context/2017/context-2017-features-kerntest.pdfbin0 -> 15259 bytes
-rw-r--r--doc/context/presentations/context/2017/context-2017-features-kerntest.tex25
-rw-r--r--doc/context/presentations/context/2017/context-2017-features-pairtest.pdfbin0 -> 15703 bytes
-rw-r--r--doc/context/presentations/context/2017/context-2017-features-pairtest.tex26
-rw-r--r--doc/context/presentations/context/2017/context-2017-features-singletest.pdfbin0 -> 15672 bytes
-rw-r--r--doc/context/presentations/context/2017/context-2017-features-singletest.tex25
-rw-r--r--doc/context/presentations/context/2017/context-2017-features-spacetest.pdfbin0 -> 15716 bytes
-rw-r--r--doc/context/presentations/context/2017/context-2017-features-spacetest.tex43
-rw-r--r--doc/context/presentations/context/2017/context-2017-features-substitutiontest.pdfbin0 -> 17567 bytes
-rw-r--r--doc/context/presentations/context/2017/context-2017-features-substitutiontest.tex141
-rw-r--r--doc/context/presentations/context/2017/context-2017-features.pdfbin0 -> 58197 bytes
-rw-r--r--doc/context/presentations/context/2017/context-2017-features.tex153
-rw-r--r--doc/context/presentations/context/2017/context-2017-performance.pdfbin0 -> 51156 bytes
-rw-r--r--doc/context/presentations/context/2017/context-2017-performance.tex65
-rw-r--r--doc/context/presentations/context/2017/context-2017-synctex.pdfbin0 -> 58760 bytes
-rw-r--r--doc/context/presentations/context/2017/context-2017-synctex.tex102
-rw-r--r--doc/context/presentations/context/2017/context-2017-tables.pdfbin0 -> 46060 bytes
-rw-r--r--doc/context/presentations/context/2017/context-2017-tables.tex175
-rw-r--r--doc/context/presentations/context/2019/context-2019-lmtx.pdfbin0 -> 41345 bytes
-rw-r--r--doc/context/presentations/context/2019/context-2019-lmtx.tex201
-rw-r--r--doc/context/sources/general/magazines/mag-1104-mkiv.tex28
-rw-r--r--doc/context/sources/general/manuals/lowlevel/lowlevel-boxes.tex698
-rw-r--r--doc/context/sources/general/manuals/lowlevel/lowlevel-conditionals.tex1409
-rw-r--r--doc/context/sources/general/manuals/lowlevel/lowlevel-expansion.tex442
-rw-r--r--doc/context/sources/general/manuals/lowlevel/lowlevel-registers.tex251
-rw-r--r--doc/context/sources/general/manuals/lowlevel/lowlevel-style.tex104
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-arrow.tex166
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-axis.tex83
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-chart.tex441
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-contents.tex11
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-contour.tex771
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-followtext.tex124
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-function.tex296
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-grid.tex11
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-interface.tex155
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-introduction.tex88
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-mesh-examples.tex161
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-mesh.tex78
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-outline.tex188
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-placeholder.tex163
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-shade.tex230
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-style.tex55
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-svg.tex75
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-text.tex137
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun-titlepage.tex41
-rw-r--r--doc/context/sources/general/manuals/luametafun/luametafun.tex31
-rw-r--r--doc/context/sources/general/manuals/luametafun/mozilla-svg-001.svg1
-rw-r--r--doc/context/sources/general/manuals/luametafun/mozilla-svg-002.svg10
-rw-r--r--doc/context/sources/general/manuals/metafun/metafun-basics.tex2
-rw-r--r--metapost/context/base/mpiv/mp-blob.mpiv11
-rw-r--r--metapost/context/base/mpiv/mp-lmtx.mpxl157
-rw-r--r--metapost/context/base/mpiv/mp-luas.mpxl2
-rw-r--r--metapost/context/base/mpiv/mp-mlib.mpiv20
-rw-r--r--metapost/context/base/mpiv/mp-tool.mpiv37
-rw-r--r--scripts/context/lua/mtx-install.lua41
-rw-r--r--scripts/context/lua/mtxrun.lua60
-rw-r--r--scripts/context/stubs/install/first-setup.sh23
-rw-r--r--scripts/context/stubs/mswin/mtxrun.lua60
-rw-r--r--scripts/context/stubs/unix/mtxrun60
-rw-r--r--scripts/context/stubs/win64/mtxrun.lua60
-rw-r--r--tex/context/base/mkii/cont-new.mkii2
-rw-r--r--tex/context/base/mkii/context.mkii2
-rw-r--r--tex/context/base/mkii/mult-en.mkii1
-rw-r--r--tex/context/base/mkii/mult-it.mkii8
-rw-r--r--tex/context/base/mkii/mult-ro.mkii1
-rw-r--r--tex/context/base/mkiv/back-ini.lua23
-rw-r--r--tex/context/base/mkiv/back-pdf.mkiv8
-rw-r--r--tex/context/base/mkiv/back-pdf.mkxl8
-rw-r--r--tex/context/base/mkiv/cont-new.mkiv2
-rw-r--r--tex/context/base/mkiv/context.mkiv2
-rw-r--r--tex/context/base/mkiv/context.mkxl2
-rw-r--r--tex/context/base/mkiv/driv-shp.lua28
-rw-r--r--tex/context/base/mkiv/font-con.lua7
-rw-r--r--tex/context/base/mkiv/font-lib.mkvi16
-rw-r--r--tex/context/base/mkiv/font-ocl.lua89
-rw-r--r--tex/context/base/mkiv/font-ogr.lua381
-rw-r--r--tex/context/base/mkiv/font-otl.lua1
-rw-r--r--tex/context/base/mkiv/font-tpk.lua28
-rw-r--r--tex/context/base/mkiv/grph-chk.lua2
-rw-r--r--tex/context/base/mkiv/grph-img.lua50
-rw-r--r--tex/context/base/mkiv/lpdf-emb.lua397
-rw-r--r--tex/context/base/mkiv/lpdf-img.lua26
-rw-r--r--tex/context/base/mkiv/lpdf-ini.lua79
-rw-r--r--tex/context/base/mkiv/lpdf-lmt.lua111
-rw-r--r--tex/context/base/mkiv/lxml-css.lua17
-rw-r--r--tex/context/base/mkiv/meta-fig.mkiv7
-rw-r--r--tex/context/base/mkiv/meta-ini.mkiv3
-rw-r--r--tex/context/base/mkiv/meta-ini.mkxl3
-rw-r--r--tex/context/base/mkiv/mlib-cnt.lua1770
-rw-r--r--tex/context/base/mkiv/mlib-ctx.lua2
-rw-r--r--tex/context/base/mkiv/mlib-ctx.mkiv3
-rw-r--r--tex/context/base/mkiv/mlib-ctx.mkxl3
-rw-r--r--tex/context/base/mkiv/mlib-lmp.lua8
-rw-r--r--tex/context/base/mkiv/mlib-lua.lua14
-rw-r--r--tex/context/base/mkiv/mlib-pdf.lua13
-rw-r--r--tex/context/base/mkiv/mlib-pps.lua6
-rw-r--r--tex/context/base/mkiv/mlib-run.lua105
-rw-r--r--tex/context/base/mkiv/mlib-scn.lua19
-rw-r--r--tex/context/base/mkiv/mlib-svg.lua1635
-rw-r--r--tex/context/base/mkiv/mult-def.lua4
-rw-r--r--tex/context/base/mkiv/mult-fun.lua1
-rw-r--r--tex/context/base/mkiv/mult-mps.lua2
-rw-r--r--tex/context/base/mkiv/status-files.pdfbin26674 -> 26690 bytes
-rw-r--r--tex/context/base/mkiv/status-lua.pdfbin268804 -> 267983 bytes
-rw-r--r--tex/context/base/mkiv/strc-itm.mklx26
-rw-r--r--tex/context/base/mkiv/strc-itm.mkvi26
-rw-r--r--tex/context/base/mkiv/strc-ref.lua16
-rw-r--r--tex/context/base/mkiv/syst-aux.mkiv2
-rw-r--r--tex/context/base/mkiv/syst-aux.mkxl2
-rw-r--r--tex/context/base/mkiv/trac-deb.lua5
-rw-r--r--tex/context/base/mkiv/util-lua.lua10
-rw-r--r--tex/context/base/mkiv/util-sac.lua57
-rw-r--r--tex/context/interface/mkii/keys-en.xml1
-rw-r--r--tex/context/interface/mkii/keys-it.xml8
-rw-r--r--tex/context/interface/mkii/keys-ro.xml1
-rw-r--r--tex/context/interface/mkiv/context-en.xml2
-rw-r--r--tex/context/interface/mkiv/i-context.pdfbin893466 -> 893435 bytes
-rw-r--r--tex/context/interface/mkiv/i-itemgroup.xml1
-rw-r--r--tex/context/interface/mkiv/i-readme.pdfbin61165 -> 61165 bytes
-rw-r--r--tex/context/modules/mkiv/m-scite.mkiv7
-rw-r--r--tex/context/modules/mkiv/s-fonts-variable.mkiv34
-rw-r--r--tex/context/modules/mkiv/s-present-luatex.mkiv118
-rw-r--r--tex/context/modules/mkiv/s-tugboat.mkiv818
-rw-r--r--tex/generic/context/luatex/luatex-basics-nod.lua3
-rw-r--r--tex/generic/context/luatex/luatex-fonts-merged.lua65
156 files changed, 13190 insertions, 943 deletions
diff --git a/context/data/scite/context/lexers/data/scite-context-data-metafun.lua b/context/data/scite/context/lexers/data/scite-context-data-metafun.lua
index 7d6256d54..c2d4440f1 100644
--- a/context/data/scite/context/lexers/data/scite-context-data-metafun.lua
+++ b/context/data/scite/context/lexers/data/scite-context-data-metafun.lua
@@ -1,4 +1,4 @@
return {
["commands"]={ "loadfile", "loadimage", "loadmodule", "dispose", "nothing", "transparency", "tolist", "topath", "tocycle", "sqr", "log", "ln", "exp", "inv", "pow", "pi", "radian", "tand", "cotd", "sin", "cos", "tan", "cot", "atan", "asin", "acos", "invsin", "invcos", "invtan", "acosh", "asinh", "sinh", "cosh", "tanh", "zmod", "paired", "tripled", "unitcircle", "fulldiamond", "unitdiamond", "fullsquare", "unittriangle", "fulltriangle", "llcircle", "lrcircle", "urcircle", "ulcircle", "tcircle", "bcircle", "lcircle", "rcircle", "lltriangle", "lrtriangle", "urtriangle", "ultriangle", "uptriangle", "downtriangle", "lefttriangle", "righttriangle", "triangle", "smoothed", "cornered", "superellipsed", "randomized", "randomizedcontrols", "squeezed", "enlonged", "shortened", "punked", "curved", "unspiked", "simplified", "blownup", "stretched", "enlarged", "leftenlarged", "topenlarged", "rightenlarged", "bottomenlarged", "crossed", "laddered", "randomshifted", "interpolated", "perpendicular", "paralleled", "cutends", "peepholed", "llenlarged", "lrenlarged", "urenlarged", "ulenlarged", "llmoved", "lrmoved", "urmoved", "ulmoved", "rightarrow", "leftarrow", "centerarrow", "drawdoublearrows", "boundingbox", "innerboundingbox", "outerboundingbox", "pushboundingbox", "popboundingbox", "boundingradius", "boundingcircle", "boundingpoint", "crossingunder", "insideof", "outsideof", "bottomboundary", "leftboundary", "topboundary", "rightboundary", "xsized", "ysized", "xysized", "sized", "xyscaled", "intersection_point", "intersection_found", "penpoint", "bbwidth", "bbheight", "withshade", "withcircularshade", "withlinearshade", "defineshade", "shaded", "shadedinto", "withshadecolors", "withshadedomain", "withshademethod", "withshadefactor", "withshadevector", "withshadecenter", "withshadedirection", "withshaderadius", "withshadetransform", "withshadestep", "withshadefraction", "withshadeorigin", "shownshadevector", "shownshadeorigin", "cmyk", "spotcolor", "multitonecolor", "namedcolor", "drawfill", "undrawfill", "inverted", "uncolored", "softened", "grayed", "greyed", "onlayer", "along", "graphictext", "loadfigure", "externalfigure", "figure", "register", "outlinetext", "filloutlinetext", "drawoutlinetext", "outlinetexttopath", "checkedbounds", "checkbounds", "strut", "rule", "withmask", "bitmapimage", "colordecimals", "ddecimal", "dddecimal", "ddddecimal", "colordecimalslist", "textext", "thetextext", "rawtextext", "textextoffset", "texbox", "thetexbox", "rawtexbox", "istextext", "notcached", "keepcached", "verbatim", "thelabel", "label", "autoalign", "transparent", "withtransparency", "property", "properties", "withproperties", "asgroup", "infont", "space", "crlf", "dquote", "percent", "SPACE", "CRLF", "DQUOTE", "PERCENT", "grayscale", "greyscale", "withgray", "withgrey", "colorpart", "colorlike", "readfile", "clearxy", "unitvector", "center", "epsed", "anchored", "originpath", "infinite", "break", "xstretched", "ystretched", "snapped", "pathconnectors", "function", "constructedfunction", "constructedpath", "constructedpairs", "straightfunction", "straightpath", "straightpairs", "curvedfunction", "curvedpath", "curvedpairs", "evenly", "oddly", "condition", "pushcurrentpicture", "popcurrentpicture", "arrowpath", "resetarrows", "tensecircle", "roundedsquare", "colortype", "whitecolor", "blackcolor", "basiccolors", "complementary", "complemented", "resolvedcolor", "normalfill", "normaldraw", "visualizepaths", "detailpaths", "naturalizepaths", "drawboundary", "drawwholepath", "drawpathonly", "visualizeddraw", "visualizedfill", "detaileddraw", "draworigin", "drawboundingbox", "drawpath", "drawpoint", "drawpoints", "drawcontrolpoints", "drawcontrollines", "drawpointlabels", "drawlineoptions", "drawpointoptions", "drawcontroloptions", "drawlabeloptions", "draworiginoptions", "drawboundoptions", "drawpathoptions", "resetdrawoptions", "undashed", "pencilled", "decorated", "redecorated", "undecorated", "passvariable", "passarrayvariable", "tostring", "topair", "format", "formatted", "quotation", "quote", "startpassingvariable", "stoppassingvariable", "eofill", "eoclip", "nofill", "dofill", "fillup", "eofillup", "nodraw", "dodraw", "area", "addbackground", "shadedup", "shadeddown", "shadedleft", "shadedright", "sortlist", "copylist", "shapedlist", "listtocurves", "listtolines", "listsize", "listlast", "uniquelist", "circularpath", "squarepath", "linearpath", "theoffset", "texmode", "systemmode", "texvar", "texstr", "isarray", "prefix", "dimension", "getmacro", "getdimen", "getcount", "gettoks", "setmacro", "setdimen", "setcount", "settoks", "positionpath", "positioncurve", "positionxy", "positionpxy", "positionwhd", "positionpage", "positionregion", "positionbox", "positionanchor", "positioninregion", "positionatanchor", "wdpart", "htpart", "dppart", "texvar", "texstr", "inpath", "pointof", "leftof", "rightof", "utflen", "utfsub", "newhash", "disposehash", "inhash", "tohash", "isarray", "prefix", "isobject", "comment", "report", "lua", "mp", "MP", "luacall", "mirrored", "mirroredabout", "scriptindex" },
- ["internals"]={ "nocolormodel", "greycolormodel", "graycolormodel", "rgbcolormodel", "cmykcolormodel", "shadefactor", "textextoffset", "textextanchor", "normaltransparent", "multiplytransparent", "screentransparent", "overlaytransparent", "softlighttransparent", "hardlighttransparent", "colordodgetransparent", "colorburntransparent", "darkentransparent", "lightentransparent", "differencetransparent", "exclusiontransparent", "huetransparent", "saturationtransparent", "colortransparent", "luminositytransparent", "ahvariant", "ahdimple", "ahfactor", "ahscale", "metapostversion", "maxdimensions", "drawoptionsfactor", "dq", "sq", "crossingscale", "crossingoption", "contextlmtxmode", "getparameters", "presetparameters", "hasparameter", "hasoption", "getparameter", "getparameterdefault", "getparametercount", "getmaxparametercount", "getparameterpath", "getparametertext", "applyparameters", "pushparameters", "popparameters", "definecolor" },
+ ["internals"]={ "nocolormodel", "greycolormodel", "graycolormodel", "rgbcolormodel", "cmykcolormodel", "shadefactor", "textextoffset", "textextanchor", "normaltransparent", "multiplytransparent", "screentransparent", "overlaytransparent", "softlighttransparent", "hardlighttransparent", "colordodgetransparent", "colorburntransparent", "darkentransparent", "lightentransparent", "differencetransparent", "exclusiontransparent", "huetransparent", "saturationtransparent", "colortransparent", "luminositytransparent", "ahvariant", "ahdimple", "ahfactor", "ahscale", "metapostversion", "maxdimensions", "drawoptionsfactor", "dq", "sq", "crossingscale", "crossingoption", "contextlmtxmode", "getparameters", "presetparameters", "hasparameter", "hasoption", "getparameter", "getparameterdefault", "getparametercount", "getmaxparametercount", "getparameterpath", "getparameterpen", "getparametertext", "applyparameters", "pushparameters", "popparameters", "definecolor" },
} \ No newline at end of file
diff --git a/context/data/scite/context/lexers/data/scite-context-data-metapost.lua b/context/data/scite/context/lexers/data/scite-context-data-metapost.lua
index fb48511b1..cd69aff8f 100644
--- a/context/data/scite/context/lexers/data/scite-context-data-metapost.lua
+++ b/context/data/scite/context/lexers/data/scite-context-data-metapost.lua
@@ -1,5 +1,5 @@
return {
- ["commands"]={ "upto", "downto", "beginfig", "endfig", "beginglyph", "endglyph", "rotatedaround", "reflectedabout", "arrowhead", "currentpen", "currentpicture", "cuttings", "defaultfont", "extra_beginfig", "extra_endfig", "down", "evenly", "fullcircle", "halfcircle", "identity", "in", "left", "pensquare", "penrazor", "penspec", "origin", "quartercircle", "right", "unitsquare", "up", "withdots", "abs", "bbox", "ceiling", "center", "cutafter", "cutbefore", "dir", "directionpoint", "div", "dotprod", "intersectionpoint", "inverse", "mod", "round", "unitvector", "whatever", "cutdraw", "draw", "drawarrow", "drawdblarrow", "fill", "filldraw", "drawdot", "loggingall", "interact", "tracingall", "tracingnone", "pickup", "undraw", "unfill", "unfilldraw", "buildcycle", "dashpattern", "decr", "dotlabel", "dotlabels", "drawoptions", "incr", "label", "labels", "max", "min", "thelabel", "z", "beginchar", "blacker", "capsule_end", "change_width", "define_blacker_pixels", "define_corrected_pixels", "define_good_x_pixels", "define_good_y_pixels", "define_horizontal_corrected_pixels", "define_pixels", "define_whole_blacker_pixels", "define_whole_pixels", "define_whole_vertical_blacker_pixels", "define_whole_vertical_pixels", "endchar", "extra_beginchar", "extra_endchar", "extra_setup", "font_coding_scheme", "clearxy", "clearit", "clearpen", "shipit", "font_extra_space", "exitunless", "relax", "hide", "gobble", "gobbled", "stop", "blankpicture", "counterclockwise", "tensepath", "takepower", "direction", "softjoin", "makelabel", "rotatedabout", "flex", "superellipse", "image", "nullpen", "savepen", "clearpen", "penpos", "penlabels", "range", "thru", "z", "laboff", "bye", "red", "green", "blue", "cyan", "magenta", "yellow", "black", "white", "background", "mm", "pt", "dd", "bp", "cm", "pc", "cc", "in", "triplet", "quadruplet", "totransform" },
+ ["commands"]={ "upto", "downto", "beginfig", "endfig", "beginglyph", "endglyph", "rotatedaround", "reflectedabout", "arrowhead", "currentpen", "currentpicture", "cuttings", "defaultfont", "extra_beginfig", "extra_endfig", "down", "evenly", "fullcircle", "halfcircle", "identity", "in", "left", "pensquare", "penrazor", "penspec", "origin", "quartercircle", "right", "unitsquare", "up", "withdots", "abs", "bbox", "ceiling", "center", "cutafter", "cutbefore", "dir", "directionpoint", "div", "dotprod", "intersectionpoint", "inverse", "mod", "round", "unitvector", "whatever", "cutdraw", "draw", "drawarrow", "drawdblarrow", "fill", "filldraw", "drawdot", "loggingall", "interact", "tracingall", "tracingnone", "pickup", "undraw", "unfill", "unfilldraw", "buildcycle", "dashpattern", "decr", "dotlabel", "dotlabels", "drawoptions", "incr", "label", "labels", "max", "min", "thelabel", "z", "beginchar", "blacker", "capsule_end", "change_width", "define_blacker_pixels", "define_corrected_pixels", "define_good_x_pixels", "define_good_y_pixels", "define_horizontal_corrected_pixels", "define_pixels", "define_whole_blacker_pixels", "define_whole_pixels", "define_whole_vertical_blacker_pixels", "define_whole_vertical_pixels", "endchar", "extra_beginchar", "extra_endchar", "extra_setup", "font_coding_scheme", "clearxy", "clearit", "clearpen", "shipit", "font_extra_space", "exitunless", "relax", "hide", "gobble", "gobbled", "stop", "blankpicture", "counterclockwise", "tensepath", "takepower", "direction", "softjoin", "makelabel", "rotatedabout", "flex", "superellipse", "image", "nullpen", "savepen", "clearpen", "penpos", "penlabels", "range", "thru", "z", "laboff", "bye", "red", "green", "blue", "cyan", "magenta", "yellow", "black", "white", "background", "mm", "pt", "dd", "bp", "cm", "pc", "cc", "in", "triplet", "quadruplet", "totransform", "bymatrix" },
["disabled"]={ "verbatimtex", "troffmode" },
["internals"]={ "mitered", "rounded", "beveled", "butt", "squared", "eps", "epsilon", "infinity", "bboxmargin", "ahlength", "ahangle", "labeloffset", "dotlabeldiam", "defaultpen", "defaultscale", "join_radius", "charscale", "ditto", "EOF", "pen_lft", "pen_rt", "pen_top", "pen_bot" },
["metafont"]={ "autorounding", "beginchar", "blacker", "boundarychar", "capsule_def", "capsule_end", "change_width", "chardp", "chardx", "chardy", "charexists", "charext", "charht", "charic", "charlist", "charwd", "cull", "cullit", "currenttransform", "currentwindow", "define_blacker_pixels", "define_corrected_pixels", "define_good_x_pixels", "define_good_y_pixels", "define_horizontal_corrected_pixels", "define_pixels", "define_whole_blacker_pixels", "define_whole_pixels", "define_whole_vertical_blacker_pixels", "define_whole_vertical_pixels", "designsize", "display", "displaying", "endchar", "extensible", "extra_beginchar", "extra_endchar", "extra_setup", "fillin", "font_coding_scheme", "font_extra_space", "font_identifier", "font_normal_shrink", "font_normal_space", "font_normal_stretch", "font_quad", "font_size", "font_slant", "font_x_height", "fontdimen", "fontmaking", "gfcorners", "granularity", "grayfont", "headerbyte", "hppp", "hround", "imagerules", "italcorr", "kern", "labelfont", "ligtable", "lowres_fix", "makebox", "makegrid", "maketicks", "mode_def", "mode_setup", "nodisplays", "notransforms", "numspecial", "o_correction", "openit", "openwindow", "pixels_per_inch", "proofing", "proofoffset", "proofrule", "proofrulethickness", "rulepen", "screenchars", "screenrule", "screenstrokes", "screen_cols", "screen_rows", "showit", "slantfont", "smode", "smoothing", "titlefont", "totalweight", "tracingedges", "tracingpens", "turningcheck", "unitpixel", "vppp", "vround", "xoffset", "yoffset" },
diff --git a/context/data/scite/context/scite-context-data-metafun.properties b/context/data/scite/context/scite-context-data-metafun.properties
index 2cdafa6e8..87647eb4c 100644
--- a/context/data/scite/context/scite-context-data-metafun.properties
+++ b/context/data/scite/context/scite-context-data-metafun.properties
@@ -85,6 +85,6 @@ ahvariant ahdimple ahfactor ahscale metapostversion \
maxdimensions drawoptionsfactor dq sq crossingscale \
crossingoption contextlmtxmode getparameters presetparameters hasparameter \
hasoption getparameter getparameterdefault getparametercount getmaxparametercount \
-getparameterpath getparametertext applyparameters pushparameters popparameters \
-definecolor
+getparameterpath getparameterpen getparametertext applyparameters pushparameters \
+popparameters definecolor
diff --git a/context/data/scite/context/scite-context-data-metapost.properties b/context/data/scite/context/scite-context-data-metapost.properties
index 9673550a3..5810cb007 100644
--- a/context/data/scite/context/scite-context-data-metapost.properties
+++ b/context/data/scite/context/scite-context-data-metapost.properties
@@ -28,7 +28,7 @@ laboff bye red green blue \
cyan magenta yellow black white \
background mm pt dd bp \
cm pc cc in triplet \
-quadruplet totransform
+quadruplet totransform bymatrix
keywordclass.metapost.disabled=\
verbatimtex troffmode
diff --git a/context/data/textadept/context/data/scite-context-data-metafun.lua b/context/data/textadept/context/data/scite-context-data-metafun.lua
index 7d6256d54..c2d4440f1 100644
--- a/context/data/textadept/context/data/scite-context-data-metafun.lua
+++ b/context/data/textadept/context/data/scite-context-data-metafun.lua
@@ -1,4 +1,4 @@
return {
["commands"]={ "loadfile", "loadimage", "loadmodule", "dispose", "nothing", "transparency", "tolist", "topath", "tocycle", "sqr", "log", "ln", "exp", "inv", "pow", "pi", "radian", "tand", "cotd", "sin", "cos", "tan", "cot", "atan", "asin", "acos", "invsin", "invcos", "invtan", "acosh", "asinh", "sinh", "cosh", "tanh", "zmod", "paired", "tripled", "unitcircle", "fulldiamond", "unitdiamond", "fullsquare", "unittriangle", "fulltriangle", "llcircle", "lrcircle", "urcircle", "ulcircle", "tcircle", "bcircle", "lcircle", "rcircle", "lltriangle", "lrtriangle", "urtriangle", "ultriangle", "uptriangle", "downtriangle", "lefttriangle", "righttriangle", "triangle", "smoothed", "cornered", "superellipsed", "randomized", "randomizedcontrols", "squeezed", "enlonged", "shortened", "punked", "curved", "unspiked", "simplified", "blownup", "stretched", "enlarged", "leftenlarged", "topenlarged", "rightenlarged", "bottomenlarged", "crossed", "laddered", "randomshifted", "interpolated", "perpendicular", "paralleled", "cutends", "peepholed", "llenlarged", "lrenlarged", "urenlarged", "ulenlarged", "llmoved", "lrmoved", "urmoved", "ulmoved", "rightarrow", "leftarrow", "centerarrow", "drawdoublearrows", "boundingbox", "innerboundingbox", "outerboundingbox", "pushboundingbox", "popboundingbox", "boundingradius", "boundingcircle", "boundingpoint", "crossingunder", "insideof", "outsideof", "bottomboundary", "leftboundary", "topboundary", "rightboundary", "xsized", "ysized", "xysized", "sized", "xyscaled", "intersection_point", "intersection_found", "penpoint", "bbwidth", "bbheight", "withshade", "withcircularshade", "withlinearshade", "defineshade", "shaded", "shadedinto", "withshadecolors", "withshadedomain", "withshademethod", "withshadefactor", "withshadevector", "withshadecenter", "withshadedirection", "withshaderadius", "withshadetransform", "withshadestep", "withshadefraction", "withshadeorigin", "shownshadevector", "shownshadeorigin", "cmyk", "spotcolor", "multitonecolor", "namedcolor", "drawfill", "undrawfill", "inverted", "uncolored", "softened", "grayed", "greyed", "onlayer", "along", "graphictext", "loadfigure", "externalfigure", "figure", "register", "outlinetext", "filloutlinetext", "drawoutlinetext", "outlinetexttopath", "checkedbounds", "checkbounds", "strut", "rule", "withmask", "bitmapimage", "colordecimals", "ddecimal", "dddecimal", "ddddecimal", "colordecimalslist", "textext", "thetextext", "rawtextext", "textextoffset", "texbox", "thetexbox", "rawtexbox", "istextext", "notcached", "keepcached", "verbatim", "thelabel", "label", "autoalign", "transparent", "withtransparency", "property", "properties", "withproperties", "asgroup", "infont", "space", "crlf", "dquote", "percent", "SPACE", "CRLF", "DQUOTE", "PERCENT", "grayscale", "greyscale", "withgray", "withgrey", "colorpart", "colorlike", "readfile", "clearxy", "unitvector", "center", "epsed", "anchored", "originpath", "infinite", "break", "xstretched", "ystretched", "snapped", "pathconnectors", "function", "constructedfunction", "constructedpath", "constructedpairs", "straightfunction", "straightpath", "straightpairs", "curvedfunction", "curvedpath", "curvedpairs", "evenly", "oddly", "condition", "pushcurrentpicture", "popcurrentpicture", "arrowpath", "resetarrows", "tensecircle", "roundedsquare", "colortype", "whitecolor", "blackcolor", "basiccolors", "complementary", "complemented", "resolvedcolor", "normalfill", "normaldraw", "visualizepaths", "detailpaths", "naturalizepaths", "drawboundary", "drawwholepath", "drawpathonly", "visualizeddraw", "visualizedfill", "detaileddraw", "draworigin", "drawboundingbox", "drawpath", "drawpoint", "drawpoints", "drawcontrolpoints", "drawcontrollines", "drawpointlabels", "drawlineoptions", "drawpointoptions", "drawcontroloptions", "drawlabeloptions", "draworiginoptions", "drawboundoptions", "drawpathoptions", "resetdrawoptions", "undashed", "pencilled", "decorated", "redecorated", "undecorated", "passvariable", "passarrayvariable", "tostring", "topair", "format", "formatted", "quotation", "quote", "startpassingvariable", "stoppassingvariable", "eofill", "eoclip", "nofill", "dofill", "fillup", "eofillup", "nodraw", "dodraw", "area", "addbackground", "shadedup", "shadeddown", "shadedleft", "shadedright", "sortlist", "copylist", "shapedlist", "listtocurves", "listtolines", "listsize", "listlast", "uniquelist", "circularpath", "squarepath", "linearpath", "theoffset", "texmode", "systemmode", "texvar", "texstr", "isarray", "prefix", "dimension", "getmacro", "getdimen", "getcount", "gettoks", "setmacro", "setdimen", "setcount", "settoks", "positionpath", "positioncurve", "positionxy", "positionpxy", "positionwhd", "positionpage", "positionregion", "positionbox", "positionanchor", "positioninregion", "positionatanchor", "wdpart", "htpart", "dppart", "texvar", "texstr", "inpath", "pointof", "leftof", "rightof", "utflen", "utfsub", "newhash", "disposehash", "inhash", "tohash", "isarray", "prefix", "isobject", "comment", "report", "lua", "mp", "MP", "luacall", "mirrored", "mirroredabout", "scriptindex" },
- ["internals"]={ "nocolormodel", "greycolormodel", "graycolormodel", "rgbcolormodel", "cmykcolormodel", "shadefactor", "textextoffset", "textextanchor", "normaltransparent", "multiplytransparent", "screentransparent", "overlaytransparent", "softlighttransparent", "hardlighttransparent", "colordodgetransparent", "colorburntransparent", "darkentransparent", "lightentransparent", "differencetransparent", "exclusiontransparent", "huetransparent", "saturationtransparent", "colortransparent", "luminositytransparent", "ahvariant", "ahdimple", "ahfactor", "ahscale", "metapostversion", "maxdimensions", "drawoptionsfactor", "dq", "sq", "crossingscale", "crossingoption", "contextlmtxmode", "getparameters", "presetparameters", "hasparameter", "hasoption", "getparameter", "getparameterdefault", "getparametercount", "getmaxparametercount", "getparameterpath", "getparametertext", "applyparameters", "pushparameters", "popparameters", "definecolor" },
+ ["internals"]={ "nocolormodel", "greycolormodel", "graycolormodel", "rgbcolormodel", "cmykcolormodel", "shadefactor", "textextoffset", "textextanchor", "normaltransparent", "multiplytransparent", "screentransparent", "overlaytransparent", "softlighttransparent", "hardlighttransparent", "colordodgetransparent", "colorburntransparent", "darkentransparent", "lightentransparent", "differencetransparent", "exclusiontransparent", "huetransparent", "saturationtransparent", "colortransparent", "luminositytransparent", "ahvariant", "ahdimple", "ahfactor", "ahscale", "metapostversion", "maxdimensions", "drawoptionsfactor", "dq", "sq", "crossingscale", "crossingoption", "contextlmtxmode", "getparameters", "presetparameters", "hasparameter", "hasoption", "getparameter", "getparameterdefault", "getparametercount", "getmaxparametercount", "getparameterpath", "getparameterpen", "getparametertext", "applyparameters", "pushparameters", "popparameters", "definecolor" },
} \ No newline at end of file
diff --git a/context/data/textadept/context/data/scite-context-data-metapost.lua b/context/data/textadept/context/data/scite-context-data-metapost.lua
index fb48511b1..cd69aff8f 100644
--- a/context/data/textadept/context/data/scite-context-data-metapost.lua
+++ b/context/data/textadept/context/data/scite-context-data-metapost.lua
@@ -1,5 +1,5 @@
return {
- ["commands"]={ "upto", "downto", "beginfig", "endfig", "beginglyph", "endglyph", "rotatedaround", "reflectedabout", "arrowhead", "currentpen", "currentpicture", "cuttings", "defaultfont", "extra_beginfig", "extra_endfig", "down", "evenly", "fullcircle", "halfcircle", "identity", "in", "left", "pensquare", "penrazor", "penspec", "origin", "quartercircle", "right", "unitsquare", "up", "withdots", "abs", "bbox", "ceiling", "center", "cutafter", "cutbefore", "dir", "directionpoint", "div", "dotprod", "intersectionpoint", "inverse", "mod", "round", "unitvector", "whatever", "cutdraw", "draw", "drawarrow", "drawdblarrow", "fill", "filldraw", "drawdot", "loggingall", "interact", "tracingall", "tracingnone", "pickup", "undraw", "unfill", "unfilldraw", "buildcycle", "dashpattern", "decr", "dotlabel", "dotlabels", "drawoptions", "incr", "label", "labels", "max", "min", "thelabel", "z", "beginchar", "blacker", "capsule_end", "change_width", "define_blacker_pixels", "define_corrected_pixels", "define_good_x_pixels", "define_good_y_pixels", "define_horizontal_corrected_pixels", "define_pixels", "define_whole_blacker_pixels", "define_whole_pixels", "define_whole_vertical_blacker_pixels", "define_whole_vertical_pixels", "endchar", "extra_beginchar", "extra_endchar", "extra_setup", "font_coding_scheme", "clearxy", "clearit", "clearpen", "shipit", "font_extra_space", "exitunless", "relax", "hide", "gobble", "gobbled", "stop", "blankpicture", "counterclockwise", "tensepath", "takepower", "direction", "softjoin", "makelabel", "rotatedabout", "flex", "superellipse", "image", "nullpen", "savepen", "clearpen", "penpos", "penlabels", "range", "thru", "z", "laboff", "bye", "red", "green", "blue", "cyan", "magenta", "yellow", "black", "white", "background", "mm", "pt", "dd", "bp", "cm", "pc", "cc", "in", "triplet", "quadruplet", "totransform" },
+ ["commands"]={ "upto", "downto", "beginfig", "endfig", "beginglyph", "endglyph", "rotatedaround", "reflectedabout", "arrowhead", "currentpen", "currentpicture", "cuttings", "defaultfont", "extra_beginfig", "extra_endfig", "down", "evenly", "fullcircle", "halfcircle", "identity", "in", "left", "pensquare", "penrazor", "penspec", "origin", "quartercircle", "right", "unitsquare", "up", "withdots", "abs", "bbox", "ceiling", "center", "cutafter", "cutbefore", "dir", "directionpoint", "div", "dotprod", "intersectionpoint", "inverse", "mod", "round", "unitvector", "whatever", "cutdraw", "draw", "drawarrow", "drawdblarrow", "fill", "filldraw", "drawdot", "loggingall", "interact", "tracingall", "tracingnone", "pickup", "undraw", "unfill", "unfilldraw", "buildcycle", "dashpattern", "decr", "dotlabel", "dotlabels", "drawoptions", "incr", "label", "labels", "max", "min", "thelabel", "z", "beginchar", "blacker", "capsule_end", "change_width", "define_blacker_pixels", "define_corrected_pixels", "define_good_x_pixels", "define_good_y_pixels", "define_horizontal_corrected_pixels", "define_pixels", "define_whole_blacker_pixels", "define_whole_pixels", "define_whole_vertical_blacker_pixels", "define_whole_vertical_pixels", "endchar", "extra_beginchar", "extra_endchar", "extra_setup", "font_coding_scheme", "clearxy", "clearit", "clearpen", "shipit", "font_extra_space", "exitunless", "relax", "hide", "gobble", "gobbled", "stop", "blankpicture", "counterclockwise", "tensepath", "takepower", "direction", "softjoin", "makelabel", "rotatedabout", "flex", "superellipse", "image", "nullpen", "savepen", "clearpen", "penpos", "penlabels", "range", "thru", "z", "laboff", "bye", "red", "green", "blue", "cyan", "magenta", "yellow", "black", "white", "background", "mm", "pt", "dd", "bp", "cm", "pc", "cc", "in", "triplet", "quadruplet", "totransform", "bymatrix" },
["disabled"]={ "verbatimtex", "troffmode" },
["internals"]={ "mitered", "rounded", "beveled", "butt", "squared", "eps", "epsilon", "infinity", "bboxmargin", "ahlength", "ahangle", "labeloffset", "dotlabeldiam", "defaultpen", "defaultscale", "join_radius", "charscale", "ditto", "EOF", "pen_lft", "pen_rt", "pen_top", "pen_bot" },
["metafont"]={ "autorounding", "beginchar", "blacker", "boundarychar", "capsule_def", "capsule_end", "change_width", "chardp", "chardx", "chardy", "charexists", "charext", "charht", "charic", "charlist", "charwd", "cull", "cullit", "currenttransform", "currentwindow", "define_blacker_pixels", "define_corrected_pixels", "define_good_x_pixels", "define_good_y_pixels", "define_horizontal_corrected_pixels", "define_pixels", "define_whole_blacker_pixels", "define_whole_pixels", "define_whole_vertical_blacker_pixels", "define_whole_vertical_pixels", "designsize", "display", "displaying", "endchar", "extensible", "extra_beginchar", "extra_endchar", "extra_setup", "fillin", "font_coding_scheme", "font_extra_space", "font_identifier", "font_normal_shrink", "font_normal_space", "font_normal_stretch", "font_quad", "font_size", "font_slant", "font_x_height", "fontdimen", "fontmaking", "gfcorners", "granularity", "grayfont", "headerbyte", "hppp", "hround", "imagerules", "italcorr", "kern", "labelfont", "ligtable", "lowres_fix", "makebox", "makegrid", "maketicks", "mode_def", "mode_setup", "nodisplays", "notransforms", "numspecial", "o_correction", "openit", "openwindow", "pixels_per_inch", "proofing", "proofoffset", "proofrule", "proofrulethickness", "rulepen", "screenchars", "screenrule", "screenstrokes", "screen_cols", "screen_rows", "showit", "slantfont", "smode", "smoothing", "titlefont", "totalweight", "tracingedges", "tracingpens", "turningcheck", "unitpixel", "vppp", "vround", "xoffset", "yoffset" },
diff --git a/context/data/vscode/extensions/context/syntaxes/context-syntax-mps.json b/context/data/vscode/extensions/context/syntaxes/context-syntax-mps.json
index 6eca349eb..c43e846d3 100644
--- a/context/data/vscode/extensions/context/syntaxes/context-syntax-mps.json
+++ b/context/data/vscode/extensions/context/syntaxes/context-syntax-mps.json
@@ -115,7 +115,7 @@
"name" : "context.number.number.mps"
},
"plain" : {
- "match" : "(z|z|yellow|withdots|white|whatever|upto|up|unitvector|unitsquare|unfilldraw|unfill|undraw|triplet|tracingnone|tracingall|totransform|thru|thelabel|tensepath|takepower|superellipse|stop|softjoin|shipit|savepen|round|rotatedaround|rotatedabout|right|relax|reflectedabout|red|range|quartercircle|quadruplet|pt|pickup|pensquare|penspec|penrazor|penpos|penlabels|pc|origin|nullpen|mod|mm|min|max|makelabel|magenta|loggingall|left|laboff|labels|label|inverse|intersectionpoint|interact|incr|in|in|image|identity|hide|halfcircle|green|gobbled|gobble|fullcircle|font_extra_space|font_coding_scheme|flex|filldraw|fill|extra_setup|extra_endfig|extra_endchar|extra_beginfig|extra_beginchar|exitunless|evenly|endglyph|endfig|endchar|drawoptions|drawdot|drawdblarrow|drawarrow|draw|downto|down|dotprod|dotlabels|dotlabel|div|directionpoint|direction|dir|define_whole_vertical_pixels|define_whole_vertical_blacker_pixels|define_whole_pixels|define_whole_blacker_pixels|define_pixels|define_horizontal_corrected_pixels|define_good_y_pixels|define_good_x_pixels|define_corrected_pixels|define_blacker_pixels|defaultfont|decr|dd|dashpattern|cyan|cuttings|cutdraw|cutbefore|cutafter|currentpicture|currentpen|counterclockwise|cm|clearxy|clearpen|clearpen|clearit|change_width|center|ceiling|cc|capsule_end|bye|buildcycle|bp|blue|blankpicture|blacker|black|beginglyph|beginfig|beginchar|bbox|background|arrowhead|abs)(?=[^a-zA-Z\u005C_@!?-ÿ])",
+ "match" : "(z|z|yellow|withdots|white|whatever|upto|up|unitvector|unitsquare|unfilldraw|unfill|undraw|triplet|tracingnone|tracingall|totransform|thru|thelabel|tensepath|takepower|superellipse|stop|softjoin|shipit|savepen|round|rotatedaround|rotatedabout|right|relax|reflectedabout|red|range|quartercircle|quadruplet|pt|pickup|pensquare|penspec|penrazor|penpos|penlabels|pc|origin|nullpen|mod|mm|min|max|makelabel|magenta|loggingall|left|laboff|labels|label|inverse|intersectionpoint|interact|incr|in|in|image|identity|hide|halfcircle|green|gobbled|gobble|fullcircle|font_extra_space|font_coding_scheme|flex|filldraw|fill|extra_setup|extra_endfig|extra_endchar|extra_beginfig|extra_beginchar|exitunless|evenly|endglyph|endfig|endchar|drawoptions|drawdot|drawdblarrow|drawarrow|draw|downto|down|dotprod|dotlabels|dotlabel|div|directionpoint|direction|dir|define_whole_vertical_pixels|define_whole_vertical_blacker_pixels|define_whole_pixels|define_whole_blacker_pixels|define_pixels|define_horizontal_corrected_pixels|define_good_y_pixels|define_good_x_pixels|define_corrected_pixels|define_blacker_pixels|defaultfont|decr|dd|dashpattern|cyan|cuttings|cutdraw|cutbefore|cutafter|currentpicture|currentpen|counterclockwise|cm|clearxy|clearpen|clearpen|clearit|change_width|center|ceiling|cc|capsule_end|bymatrix|bye|buildcycle|bp|blue|blankpicture|blacker|black|beginglyph|beginfig|beginchar|bbox|background|arrowhead|abs)(?=[^a-zA-Z\u005C_@!?-ÿ])",
"name" : "context.plain.plain.mps"
},
"primitive" : {
@@ -138,7 +138,7 @@
"name" : "context.string.string.text.mps"
},
"shortcut" : {
- "match" : "(textextoffset|textextanchor|squared|sq|softlighttransparent|shadefactor|screentransparent|saturationtransparent|rounded|rgbcolormodel|pushparameters|presetparameters|popparameters|pen_top|pen_rt|pen_lft|pen_bot|overlaytransparent|normaltransparent|nocolormodel|multiplytransparent|mitered|metapostversion|maxdimensions|luminositytransparent|lightentransparent|labeloffset|join_radius|infinity|huetransparent|hasparameter|hasoption|hardlighttransparent|greycolormodel|graycolormodel|getparametertext|getparameters|getparameterpath|getparameterdefault|getparametercount|getparameter|getmaxparametercount|exclusiontransparent|epsilon|eps|drawoptionsfactor|dq|dotlabeldiam|ditto|differencetransparent|definecolor|defaultscale|defaultpen|darkentransparent|crossingscale|crossingoption|contextlmtxmode|colortransparent|colordodgetransparent|colorburntransparent|cmykcolormodel|charscale|butt|beveled|bboxmargin|applyparameters|ahvariant|ahscale|ahlength|ahfactor|ahdimple|ahangle|EOF)(?=[^a-zA-Z\u005C_@!?-ÿ])",
+ "match" : "(textextoffset|textextanchor|squared|sq|softlighttransparent|shadefactor|screentransparent|saturationtransparent|rounded|rgbcolormodel|pushparameters|presetparameters|popparameters|pen_top|pen_rt|pen_lft|pen_bot|overlaytransparent|normaltransparent|nocolormodel|multiplytransparent|mitered|metapostversion|maxdimensions|luminositytransparent|lightentransparent|labeloffset|join_radius|infinity|huetransparent|hasparameter|hasoption|hardlighttransparent|greycolormodel|graycolormodel|getparametertext|getparameters|getparameterpen|getparameterpath|getparameterdefault|getparametercount|getparameter|getmaxparametercount|exclusiontransparent|epsilon|eps|drawoptionsfactor|dq|dotlabeldiam|ditto|differencetransparent|definecolor|defaultscale|defaultpen|darkentransparent|crossingscale|crossingoption|contextlmtxmode|colortransparent|colordodgetransparent|colorburntransparent|cmykcolormodel|charscale|butt|beveled|bboxmargin|applyparameters|ahvariant|ahscale|ahlength|ahfactor|ahdimple|ahangle|EOF)(?=[^a-zA-Z\u005C_@!?-ÿ])",
"name" : "context.data.shortcut.mps"
},
"special" : {
diff --git a/doc/context/documents/general/magazines/mag-1104-mkiv.pdf b/doc/context/documents/general/magazines/mag-1104-mkiv.pdf
index ee73daea5..523ba75b2 100644
--- a/doc/context/documents/general/magazines/mag-1104-mkiv.pdf
+++ b/doc/context/documents/general/magazines/mag-1104-mkiv.pdf
Binary files differ
diff --git a/doc/context/documents/general/manuals/lowlevel-boxes.pdf b/doc/context/documents/general/manuals/lowlevel-boxes.pdf
new file mode 100644
index 000000000..305e6beba
--- /dev/null
+++ b/doc/context/documents/general/manuals/lowlevel-boxes.pdf
Binary files differ
diff --git a/doc/context/documents/general/manuals/lowlevel-conditionals.pdf b/doc/context/documents/general/manuals/lowlevel-conditionals.pdf
new file mode 100644
index 000000000..5e3d82cee
--- /dev/null
+++ b/doc/context/documents/general/manuals/lowlevel-conditionals.pdf
Binary files differ
diff --git a/doc/context/documents/general/manuals/lowlevel-expansion.pdf b/doc/context/documents/general/manuals/lowlevel-expansion.pdf
new file mode 100644
index 000000000..8628f4896
--- /dev/null
+++ b/doc/context/documents/general/manuals/lowlevel-expansion.pdf
Binary files differ
diff --git a/doc/context/documents/general/manuals/lowlevel-registers.pdf b/doc/context/documents/general/manuals/lowlevel-registers.pdf
new file mode 100644
index 000000000..d00daa0cc
--- /dev/null
+++ b/doc/context/documents/general/manuals/lowlevel-registers.pdf
Binary files differ
diff --git a/doc/context/documents/general/manuals/luametafun.pdf b/doc/context/documents/general/manuals/luametafun.pdf
new file mode 100644
index 000000000..e2b99ab2f
--- /dev/null
+++ b/doc/context/documents/general/manuals/luametafun.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-cs.pdf b/doc/context/documents/general/qrcs/setup-cs.pdf
index c42a21d13..d2f987d97 100644
--- a/doc/context/documents/general/qrcs/setup-cs.pdf
+++ b/doc/context/documents/general/qrcs/setup-cs.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-de.pdf b/doc/context/documents/general/qrcs/setup-de.pdf
index 5e7b80ac8..320d8ca11 100644
--- a/doc/context/documents/general/qrcs/setup-de.pdf
+++ b/doc/context/documents/general/qrcs/setup-de.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-en.pdf b/doc/context/documents/general/qrcs/setup-en.pdf
index a6e83085f..5c0fba19a 100644
--- a/doc/context/documents/general/qrcs/setup-en.pdf
+++ b/doc/context/documents/general/qrcs/setup-en.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-fr.pdf b/doc/context/documents/general/qrcs/setup-fr.pdf
index 71cb7eaa8..30c60469e 100644
--- a/doc/context/documents/general/qrcs/setup-fr.pdf
+++ b/doc/context/documents/general/qrcs/setup-fr.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-it.pdf b/doc/context/documents/general/qrcs/setup-it.pdf
index f8df2036f..f02842253 100644
--- a/doc/context/documents/general/qrcs/setup-it.pdf
+++ b/doc/context/documents/general/qrcs/setup-it.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-mapping-cs.pdf b/doc/context/documents/general/qrcs/setup-mapping-cs.pdf
index 6ee9c83d2..fb720a611 100644
--- a/doc/context/documents/general/qrcs/setup-mapping-cs.pdf
+++ b/doc/context/documents/general/qrcs/setup-mapping-cs.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-mapping-de.pdf b/doc/context/documents/general/qrcs/setup-mapping-de.pdf
index bf1adfd0d..225593507 100644
--- a/doc/context/documents/general/qrcs/setup-mapping-de.pdf
+++ b/doc/context/documents/general/qrcs/setup-mapping-de.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-mapping-en.pdf b/doc/context/documents/general/qrcs/setup-mapping-en.pdf
index 130408cf9..ad12ac66c 100644
--- a/doc/context/documents/general/qrcs/setup-mapping-en.pdf
+++ b/doc/context/documents/general/qrcs/setup-mapping-en.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-mapping-fr.pdf b/doc/context/documents/general/qrcs/setup-mapping-fr.pdf
index 7087bb0e2..07e1a5f7c 100644
--- a/doc/context/documents/general/qrcs/setup-mapping-fr.pdf
+++ b/doc/context/documents/general/qrcs/setup-mapping-fr.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-mapping-it.pdf b/doc/context/documents/general/qrcs/setup-mapping-it.pdf
index 5f803464f..97b9586f7 100644
--- a/doc/context/documents/general/qrcs/setup-mapping-it.pdf
+++ b/doc/context/documents/general/qrcs/setup-mapping-it.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-mapping-nl.pdf b/doc/context/documents/general/qrcs/setup-mapping-nl.pdf
index f8f5cfa5b..3a7de7431 100644
--- a/doc/context/documents/general/qrcs/setup-mapping-nl.pdf
+++ b/doc/context/documents/general/qrcs/setup-mapping-nl.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-mapping-ro.pdf b/doc/context/documents/general/qrcs/setup-mapping-ro.pdf
index 07dbe708d..9f837fa1b 100644
--- a/doc/context/documents/general/qrcs/setup-mapping-ro.pdf
+++ b/doc/context/documents/general/qrcs/setup-mapping-ro.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-nl.pdf b/doc/context/documents/general/qrcs/setup-nl.pdf
index 075e71cd9..1718a6b3d 100644
--- a/doc/context/documents/general/qrcs/setup-nl.pdf
+++ b/doc/context/documents/general/qrcs/setup-nl.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-ro.pdf b/doc/context/documents/general/qrcs/setup-ro.pdf
index 79a6d0a9f..6682a9bcb 100644
--- a/doc/context/documents/general/qrcs/setup-ro.pdf
+++ b/doc/context/documents/general/qrcs/setup-ro.pdf
Binary files differ
diff --git a/doc/context/presentations/context/2017/context-2017-css-selectors.pdf b/doc/context/presentations/context/2017/context-2017-css-selectors.pdf
new file mode 100644
index 000000000..d4575f0e7
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-css-selectors.pdf
Binary files differ
diff --git a/doc/context/presentations/context/2017/context-2017-css-selectors.tex b/doc/context/presentations/context/2017/context-2017-css-selectors.tex
new file mode 100644
index 000000000..845206655
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-css-selectors.tex
@@ -0,0 +1,238 @@
+\usemodule[present-lines]
+
+\definecolor[maincolor][b=.4]
+
+\setuppapersize[A4][A4]
+
+\startdocument[title=CSS selectors,subtitle={\CONTEXT\ 2017 Maibach}]
+
+\startbuffer[selector-001]
+<?xml version="1.0" ?>
+
+<a>
+ <b class="one">b.one</b>
+ <b class="two">b.two</b>
+ <b class="one two">b.one.two</b>
+ <b class="three">b.three</b>
+ <b id="first">b#first</b>
+ <c>c</c>
+ <d>d e</d>
+ <e>d e</e>
+ <e>d e e</e>
+ <d>d f</d>
+ <f foo="bar">@foo = bar</f>
+ <f bar="foo">@bar = foo</f>
+ <f bar="foo1">@bar = foo1</f>
+ <f bar="foo2">@bar = foo2</f>
+ <f bar="foo3">@bar = foo3</f>
+ <f bar="foo+4">@bar = foo+4</f>
+ <g>g</g>
+ <g><gg><d>g gg d</d></gg></g>
+ <g><gg><f>g gg f</f></gg></g>
+ <g><gg><f class="one">g gg f.one</f></gg></g>
+ <g>g</g>
+ <g><gg><f class="two">g gg f.two</f></gg></g>
+ <g><gg><f class="three">g gg f.three</f></gg></g>
+ <g><f class="one">g f.one</f></g>
+ <g><f class="three">g f.three</f></g>
+ <h whatever="four five six">@whatever = four five six</h>
+</a>
+\stopbuffer
+
+\startbuffer[selector-002]
+<?xml version="1.0" ?>
+
+<document>
+ <title class="one" >title 1</title>
+ <title class="two" >title 2</title>
+ <title class="one" >title 3</title>
+ <title class="three">title 4</title>
+</document>
+\stopbuffer
+
+\startbuffer[selector-003]
+<?xml version="1.0" ?>
+
+<document>
+ <title class="one">title 1</title>
+ <subtitle class="sub">title 1.1</subtitle>
+ <title class="two">title 2</title>
+ <subtitle class="sub">title 2.1</subtitle>
+ <title class="one">title 3</title>
+ <subtitle class="sub">title 3.1</subtitle>
+ <title class="two">title 4</title>
+ <subtitle class="sub">title 4.1</subtitle>
+</document>
+\stopbuffer
+
+\xmlloadbuffer{selector-001}{selector-001}
+\xmlloadbuffer{selector-002}{selector-002}
+\xmlloadbuffer{selector-003}{selector-003}
+
+\startxmlsetups xml:selector:demo
+ \ignorespaces\xmlverbatim{#1}\par
+\stopxmlsetups
+
+\unexpanded\def\showCSSdemo#1#2%
+ {\blank
+ \textrule{\tttf#2}
+ \startlines
+ \dontcomplain
+ \tttf \obeyspaces
+ \xmlcommand{#1}{#2}{xml:selector:demo}
+ \stoplines
+ \blank}
+
+\startchapter[title=Needed or not?]
+
+\startitemize
+\startitem
+ used in \HTML\ styling
+\stopitem
+\startitem
+ a bit different from the path based method
+\stopitem
+\startitem
+ shortcuts for filtering by attribute
+\stopitem
+\startitem
+ class filtering is special because it checks for list
+\stopitem
+\startitem
+ one can select more at the same time
+\stopitem
+\startitem
+ performance is okay compared to path lookup
+\stopitem
+\startitem
+ selectors go between curly braces:
+ \starttyping
+ \xmlall {#1} {{foo bar .whatever, bar foo .whatever}}
+ \stoptyping
+\stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=Supported methods]
+
+The following methods are supported:
+
+\starttabulate[|T||]
+\NC \type {element} \NC all tags element \NC \NR
+\NC \type {element-1 > element-2} \NC all tags element-2 with parent tag element-1 \NC \NR
+\NC \type {element-1 + element-2} \NC all tags element-2 preceded by tag element-1 \NC \NR
+\NC \type {element-1 ~ element-2} \NC all tags element-2 preceded by tag element-1 \NC \NR
+\NC \type {element-1 element-2} \NC all tags element-2 inside tag element-1 \NC \NR
+\NC \type {[attribute]} \NC has attribute \NC \NR
+\NC \type {[attribute=value]} \NC attribute equals value\NC \NR
+\NC \type {[attribute~=value]} \NC attribute contains value (space is separator) \NC \NR
+\NC \type {[attribute^="value"]} \NC attribute starts with value \NC \NR
+\NC \type {[attribute$="value"]} \NC attribute ends with value \NC \NR
+\NC \type {[attribute*="value"]} \NC attribute contains value \NC \NR
+\NC \type {.class} \NC has class \NC \NR
+\NC \expanded{\type {\letterhash id}} \NC has id \NC \NR
+\NC \type {:nth-child(n)} \NC the child at index n \NC \NR
+\NC \type {:nth-last-child(n)} \NC the child at index n from the end \NC \NR
+\NC \type {:first-child} \NC the first child \NC \NR
+\NC \type {:last-child} \NC the last child \NC \NR
+\NC \type {:nth-of-type(n)} \NC the match at index n \NC \NR
+\NC \type {:nth-last-of-type(n)} \NC the match at index n from the end \NC \NR
+\NC \type {:first-of-type} \NC the first match \NC \NR
+\NC \type {:last-of-type} \NC the last match \NC \NR
+\NC \type {:only-of-type} \NC the only match or nothing \NC \NR
+\NC \type {:only-child} \NC the only child or nothing \NC \NR
+\NC \type {:empty} \NC only when empty \NC \NR
+\NC \type {:root} \NC the whole tree \NC \NR
+\stoptabulate
+
+\stopchapter
+
+\startchapter[title=Filtering classes]
+
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{.one}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{.one, .two}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{.one, .two, \letterhash first}} \page
+
+\stopchapter
+
+\startchapter[title=Filtering attributes]
+
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{[foo], [bar=foo]}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{[bar\lettertilde=foo]}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{[bar\letterhat="foo"]}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{[whatever\lettertilde="five"]}} \page
+
+\stopchapter
+
+\startchapter[title=Combining methods]
+
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{g f .one, g f .three}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{g > f .one, g > f .three}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{d + e}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{d ~ e}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{d ~ e, g f .one, g f .three}} \page
+
+\stopchapter
+
+% \startchapter[title=Negation]
+
+% \typebuffer[selector-001] \showCSSdemo{selector-001}{{:not([whatever\lettertilde="five"])}} \page
+% \typebuffer[selector-001] \showCSSdemo{selector-001}{{:not(d)}} \page
+
+\stopchapter
+
+\startchapter[title=Child selectors]
+
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{a:nth-child(3)}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{a:nth-last-child(3)}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{g:nth-of-type(3)}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{g:nth-last-of-type(3)}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{a:first-child}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{a:last-child}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{e:first-of-type}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{gg d:only-of-type}} \page
+
+\stopchapter
+
+\startchapter[title=Simple formulas]
+
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{a:nth-child(even)}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{a:nth-child(odd)}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{a:nth-child(3n+1)}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{a:nth-child(2n+3)}} \page
+
+\stopchapter
+
+\startchapter[title=Special cases]
+
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{g:empty}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{g:root}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{*}} \page
+
+\stopchapter
+
+\startchapter[title=Combinations]
+
+\typebuffer[selector-001] \showCSSdemo{selector-001}{{g gg f .one}} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{g/gg/f[@class='one']} \page
+\typebuffer[selector-001] \showCSSdemo{selector-001}{g/{gg f .one}} \page
+
+\stopchapter
+
+\startchapter[title=Comparison (1)]
+
+\typebuffer[selector-002] \showCSSdemo{selector-002}{{document title .one, document title .three}} \page
+\typebuffer[selector-002] \showCSSdemo{selector-002}{/document/title[(@class='one') or (@class='three')]} \page
+
+\stopchapter
+
+\startchapter[title=Comparison (2)]
+
+\typebuffer[selector-003] \showCSSdemo{selector-003}{{document title .one + subtitle, document title .two + subtitle}}
+
+{\em A combined filter triggers a sorting pass!}
+
+\stopchapter
+
+\stopdocument
diff --git a/doc/context/presentations/context/2017/context-2017-features-chaintest.pdf b/doc/context/presentations/context/2017/context-2017-features-chaintest.pdf
new file mode 100644
index 000000000..1da70b355
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-features-chaintest.pdf
Binary files differ
diff --git a/doc/context/presentations/context/2017/context-2017-features-chaintest.tex b/doc/context/presentations/context/2017/context-2017-features-chaintest.tex
new file mode 100644
index 000000000..58d1040f2
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-features-chaintest.tex
@@ -0,0 +1,49 @@
+\setupbodyfont[dejavu]
+
+\startluacode
+ fonts.handlers.otf.addfeature {
+ name = "chaintest",
+ type = "chainposition",
+ lookups = {
+ {
+ type = "pair",
+ data = {
+ A = { B = { { -30, 0, -50, 0 } } },
+ B = { C = { { -30, 0, -50, 0 } } },
+ },
+ },
+ {
+ type = "pair",
+ data = {
+ D = { E = { { -30, 0, -50, 0 } } },
+ },
+ },
+ },
+ data = {
+ rules = {
+ {
+ current = { { "A"}, { "B"}, { "C" }, { "D" }, { "E" }, { "F" } },
+ lookups = { false, false, false, 2 },
+ },
+ {
+ current = { { "A" }, { "B" }, { "C" } },
+ lookups = { 1, 1 },
+ },
+ },
+ }
+ }
+\stopluacode
+
+\starttext
+
+\definecolor[tgray][s=.5,t=.5,a=1] \showfontkerns \showglyphs
+
+\definefontfeature[chaintest] [mode=node,chaintest=yes]
+
+\definedfont[file:dejavu-serif.ttf*chaintest @ 48pt]
+
+\startTEXpage[offset=10pt,foregroundcolor=tgray]
+ ABCDEF
+\stopTEXpage
+
+\stoptext
diff --git a/doc/context/presentations/context/2017/context-2017-features-kerntest.pdf b/doc/context/presentations/context/2017/context-2017-features-kerntest.pdf
new file mode 100644
index 000000000..5a8d0d38d
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-features-kerntest.pdf
Binary files differ
diff --git a/doc/context/presentations/context/2017/context-2017-features-kerntest.tex b/doc/context/presentations/context/2017/context-2017-features-kerntest.tex
new file mode 100644
index 000000000..795af8b5f
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-features-kerntest.tex
@@ -0,0 +1,25 @@
+\setupbodyfont[dejavu]
+
+\startluacode
+ fonts.handlers.otf.addfeature {
+ name = "kerntest",
+ type = "kern",
+ data = {
+ A = { B = -500 },
+ }
+ }
+\stopluacode
+
+\starttext
+
+\definecolor[tgray][s=.5,t=.5,a=1] \showfontkerns \showglyphs
+
+\definefontfeature[kerntest] [mode=node,kerntest=yes]
+
+\definedfont[file:dejavu-serif.ttf*kerntest @ 48pt]
+
+\startTEXpage[offset=10pt,foregroundcolor=tgray]
+ ABCDEF
+\stopTEXpage
+
+\stoptext
diff --git a/doc/context/presentations/context/2017/context-2017-features-pairtest.pdf b/doc/context/presentations/context/2017/context-2017-features-pairtest.pdf
new file mode 100644
index 000000000..ba959e4f7
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-features-pairtest.pdf
Binary files differ
diff --git a/doc/context/presentations/context/2017/context-2017-features-pairtest.tex b/doc/context/presentations/context/2017/context-2017-features-pairtest.tex
new file mode 100644
index 000000000..e84851e7d
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-features-pairtest.tex
@@ -0,0 +1,26 @@
+\setupbodyfont[dejavu]
+
+\startluacode
+ fonts.handlers.otf.addfeature {
+ name = "pairtest",
+ type = "pair",
+ data = {
+ A = { B = { { -30, 50, -50, 0 } } },
+ B = { C = { { -30, 0, -50, 0 } } },
+ }
+ }
+\stopluacode
+
+\starttext
+
+\definecolor[tgray][s=.5,t=.5,a=1] \showfontkerns \showglyphs
+
+\definefontfeature[pairtest] [mode=node,pairtest=yes]
+
+\definedfont[file:dejavu-serif.ttf*pairtest @ 48pt]
+
+\startTEXpage[offset=10pt,foregroundcolor=tgray]
+ ABCDEF
+\stopTEXpage
+
+\stoptext
diff --git a/doc/context/presentations/context/2017/context-2017-features-singletest.pdf b/doc/context/presentations/context/2017/context-2017-features-singletest.pdf
new file mode 100644
index 000000000..624ee547f
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-features-singletest.pdf
Binary files differ
diff --git a/doc/context/presentations/context/2017/context-2017-features-singletest.tex b/doc/context/presentations/context/2017/context-2017-features-singletest.tex
new file mode 100644
index 000000000..fb0326fa3
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-features-singletest.tex
@@ -0,0 +1,25 @@
+\setupbodyfont[dejavu]
+
+\startluacode
+ fonts.handlers.otf.addfeature {
+ name = "singletest",
+ type = "single",
+ data = {
+ B = { -30, 0, -50, 0 },
+ }
+ }
+\stopluacode
+
+\starttext
+
+\definecolor[tgray][s=.5,t=.5,a=1] \showfontkerns \showglyphs
+
+\definefontfeature[singletest] [mode=node,singletest=yes]
+
+\definedfont[file:dejavu-serif.ttf*singletest @ 48pt]
+
+\startTEXpage[offset=10pt,foregroundcolor=tgray]
+ ABCDEF
+\stopTEXpage
+
+\stoptext
diff --git a/doc/context/presentations/context/2017/context-2017-features-spacetest.pdf b/doc/context/presentations/context/2017/context-2017-features-spacetest.pdf
new file mode 100644
index 000000000..9a2c7b012
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-features-spacetest.pdf
Binary files differ
diff --git a/doc/context/presentations/context/2017/context-2017-features-spacetest.tex b/doc/context/presentations/context/2017/context-2017-features-spacetest.tex
new file mode 100644
index 000000000..48ad4e32f
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-features-spacetest.tex
@@ -0,0 +1,43 @@
+\setupbodyfont[dejavu]
+
+\startluacode
+
+ -- we could populate this one mostly automatic if needed
+ -- but also expect a font to have such kerns
+
+ local kern = -50
+ local pair = { [32] = kern }
+
+ fonts.handlers.otf.addfeature {
+ name = "kern", -- spacekerns assume kern (for now)
+ type = "kern",
+ data = {
+ D = pair,
+ E = pair,
+ F = pair,
+ [32] = {
+ D = kern,
+ E = kern,
+ F = kern,
+ },
+ }
+ }
+
+\stopluacode
+
+\starttext
+
+\enabledirectives[fonts.injections.useitalics] % use italic kerns for tracing
+
+\definecolor[tgray][s=.5,t=.5,a=1] \showfontkerns \showfontitalics \showglyphs
+
+\definefontfeature[kern] [mode=node,kern=yes,spacekerns=yes]
+
+\definedfont[file:dejavu-serif.ttf*kern @ 48pt]
+\definedfont[file:dejavu-serif.ttf*default @ 48pt]
+
+\startTEXpage[offset=10pt,foregroundcolor=tgray]
+ A B C D E F G H
+\stopTEXpage
+
+\stoptext
diff --git a/doc/context/presentations/context/2017/context-2017-features-substitutiontest.pdf b/doc/context/presentations/context/2017/context-2017-features-substitutiontest.pdf
new file mode 100644
index 000000000..b95e531ec
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-features-substitutiontest.pdf
Binary files differ
diff --git a/doc/context/presentations/context/2017/context-2017-features-substitutiontest.tex b/doc/context/presentations/context/2017/context-2017-features-substitutiontest.tex
new file mode 100644
index 000000000..9e2657a6e
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-features-substitutiontest.tex
@@ -0,0 +1,141 @@
+\startluacode
+ fonts.handlers.otf.addfeature {
+ name = "singlesubstitution",
+ type = "substitution",
+ data = {
+ a = "X",
+ b = "P",
+ }
+ }
+\stopluacode
+
+\startluacode
+ fonts.handlers.otf.addfeature {
+ name = "alternatesubstitution",
+ type = "alternate",
+ data = {
+ a = { "X", "Y" },
+ b = { "P", "Q" },
+ }
+ }
+\stopluacode
+
+\startluacode
+ fonts.handlers.otf.addfeature {
+ name = "multiplesubstitution",
+ type = "multiple",
+ data = {
+ a = { "X", "Y" },
+ b = { "P", "Q" },
+ }
+ }
+\stopluacode
+
+\startluacode
+ fonts.handlers.otf.addfeature {
+ name = "ligaturesubstitution",
+ type = "ligature",
+ data = {
+ ['1'] = { "a", "b" },
+ ['2'] = { "d", "a" },
+ }
+ }
+\stopluacode
+
+\startluacode
+ fonts.handlers.otf.addfeature {
+ name = "chainsubstitution-1",
+ type = "chainsubstitution",
+ -- flags = { false, false, false, false },
+ lookups = {
+ {
+ type = "substitution",
+ data = {
+ ["b"] = "B",
+ ["c"] = "C",
+ },
+ },
+ },
+ -- steps = {
+ -- {
+ -- rules = {
+ -- {
+ -- before = { { "a" } },
+ -- current = { { "b", "c" } },
+ -- lookups = { 1 },
+ -- },
+ -- },
+ -- },
+ -- },
+ data = {
+ rules = {
+ {
+ before = { { "a" } },
+ current = { { "b", "c" } },
+ lookups = { 1 },
+ },
+ },
+ },
+ }
+\stopluacode
+
+\startluacode
+ fonts.handlers.otf.addfeature {
+ name = "chainsubstitution-2",
+ type = "chainsubstitution",
+ prepend = 1,
+ lookups = {
+ {
+ type = "multiple",
+ data = {
+ ["f"] = { "f", 0x200C },
+ },
+ },
+ {
+ type = "substitution",
+ data = {
+ ["a"] = "1",
+ ["b"] = "2",
+ ["c"] = "3",
+ },
+ },
+ },
+ data = {
+ rules = {
+ {
+ current = { { "f" }, { "f" } },
+ lookups = { 1 },
+ },
+ {
+ current = { { "a" }, { "b" }, { "c" } },
+ lookups = { 2, false, 2 },
+ },
+ },
+ }
+ }
+\stopluacode
+
+\definefontfeature[singlesubstitution] [singlesubstitution=yes]
+\definefontfeature[alternatesubstitution][alternatesubstitution=2]
+\definefontfeature[multiplesubstitution] [multiplesubstitution=yes]
+\definefontfeature[ligaturesubstitution] [ligaturesubstitution=yes]
+\definefontfeature[chainsubstitution-1] [chainsubstitution-1=yes]
+\definefontfeature[chainsubstitution-2] [chainsubstitution-2=yes]
+
+\setupbodyfont[dejavu]
+
+\starttext
+
+\definedfont[Serif*default] \showfontkerns \showfontitalics \showglyphs
+
+\startTEXpage
+ abracadabra\par
+ {\addff {singlesubstitution}abracadabra\par}
+ {\addff{alternatesubstitution}abracadabra\par}
+ {\addff {multiplesubstitution}abracadabra\par}
+ {\addff {ligaturesubstitution}abracadabra\par}
+ {\addff {chainsubstitution-1}abracadabra\par}
+ {\addff {chainsubstitution-2}effe abcdef !f\par}
+\stopTEXpage
+
+\stoptext
diff --git a/doc/context/presentations/context/2017/context-2017-features.pdf b/doc/context/presentations/context/2017/context-2017-features.pdf
new file mode 100644
index 000000000..e70afbed7
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-features.pdf
Binary files differ
diff --git a/doc/context/presentations/context/2017/context-2017-features.tex b/doc/context/presentations/context/2017/context-2017-features.tex
new file mode 100644
index 000000000..772de4ff6
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-features.tex
@@ -0,0 +1,153 @@
+\usemodule[present-lines]
+
+\definecolor[maincolor][r=.4,g=.4]
+
+\startdocument[title=Font features,subtitle={\CONTEXT\ 2017 Maibach}]
+
+\startchapter[title=What are they]
+
+\startitemize
+ \startitem
+ built in substitution that is often optional like ligatures but for some
+ languages mandate
+ \stopitem
+ \startitem
+ built in positioning that is assumed to be applied like kerning, mark
+ anchoring cursive
+ \stopitem
+ \startitem
+ external properties like coloring, spacing, fallback combinations
+ \stopitem
+ \startitem
+ engine related tricks like expansion and protrusion
+ \stopitem
+ \startitem
+ tracing options
+ \stopitem
+ \startitem
+ whatever you like \unknown\ so let me know
+ \stopitem
+ \blank[2*big]
+ \startitem
+ so in \CONTEXT\ we have font features (driven by font) and pseudo
+ features (driven by additional needs)
+ \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=Substitution]
+
+\startitemize
+ \startitem
+ single: replace one by another
+ \stopitem
+ \startitem
+ alternate: replace one by one of a set
+ \stopitem
+ \startitem
+ multiple: replace one by multiple others
+ \stopitem
+ \startitem
+ ligature: replace multiple by one shape
+ \stopitem
+ \blank[2*big]
+ \startitem
+ contextual lookups and replacements with look back and look ahead
+ \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=Positioning]
+
+\startitemize
+ \startitem
+ single: repositioning a glyph (with optional marks), this includes
+ traditional kerning
+ \stopitem
+ \startitem
+ pairwise: repositioning two adjacent glyphs (with optional marks)
+ \stopitem
+ \startitem
+ anchoring: often used for marks to base glyphs, ligatures and other marks
+ \stopitem
+ \startitem
+ attachment: often used for cursive scripts, pasting glyphs in a word together
+ \stopitem
+ \blank[2*big]
+ \startitem
+ contextual lookups and positioning with look back and look ahead
+ \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=Related]
+
+\startitemize
+ \startitem
+ analyze: needed for dealing with features that need information about
+ initial, medial, final and isolated properties
+ \stopitem
+ \startitem
+ reordering: needed for script like devanagari
+ \stopitem
+ \startitem
+ spacing: deals with for positioning glyphs and spaces
+ \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=Pitfalls]
+
+\startitemize
+ \startitem
+ solutions for similar tasks can be quite different which makes tracing
+ or checking sometimes hard (many ways to make ligatures)
+ \stopitem
+ \startitem
+ order matters and demands careful font design but it is hard to predict
+ all cases
+ \stopitem
+ \startitem
+ a sloppy font design can result in a performance hit or huge fonts
+ \stopitem
+ \startitem
+ features can be bugged and fonts vendors seldom have an update policy
+ \stopitem
+ \startitem
+ shapers can differ due to assumptions, heuristics, interpreting
+ specifications, bugs, \unknown
+ \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=Examples]
+ \startitem
+ all kind of substitutions: \type {2017-features-substitutiontest.tex}
+ \stopitem
+ \startitem
+ simple inter character kerns: \type {2017-features-kerntest.tex}
+ \stopitem
+ \startitem
+ single character positioning: \type {2017-features-singletest.tex}
+ \stopitem
+ \startitem
+ pairwise character positioning: \type {2017-features-pairtest.tex}
+ \stopitem
+ \startitem
+ contextual positioning: \type {2017-features-contexttest.tex}
+ \stopitem
+ \startitem
+ kerning with space (glue): \type {2017-features-spacetest.tex}
+ \stopitem
+\startitemize
+
+\stopitemize
+
+\stopchapter
+
+\stopdocument
diff --git a/doc/context/presentations/context/2017/context-2017-performance.pdf b/doc/context/presentations/context/2017/context-2017-performance.pdf
new file mode 100644
index 000000000..9656e2f0e
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-performance.pdf
Binary files differ
diff --git a/doc/context/presentations/context/2017/context-2017-performance.tex b/doc/context/presentations/context/2017/context-2017-performance.tex
new file mode 100644
index 000000000..458a051a5
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-performance.tex
@@ -0,0 +1,65 @@
+\usemodule[present-lines]
+
+\definecolor[maincolor][g=.4,b=.4]
+
+\startdocument[title=Performance,subtitle={\CONTEXT\ 2017 Maibach}]
+
+\startchapter[title=Why bother]
+
+\startitemize
+ \startitem because we don't want to waste time waiting \stopitem
+ \startitem because I get tired of ignorance related complaints \stopitem
+ \startitem because it (sometimes) can be a nice puzzle to improve performance \stopitem
+ \startitem because it occasionally reviewing makes code better \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=Possible bottlenecks]
+
+\startitemize
+ \startitem starting up \stopitem
+ \startitem loading fonts \stopitem
+ \startitem processing features \stopitem
+ \startitem applying trickery \stopitem
+ \startitem enabling tracing \stopitem
+ \blank[2*big]
+ \startitem fonts \stopitem
+ \startitem \LUA \stopitem
+ \startitem images \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=Measurements]
+
+\startitemize
+ \startitem how long does a run take \stopitem
+ \startitem how does the number of pages matter \stopitem
+ \startitem how many runs are needed \stopitem
+ \blank[2*big]
+ \startitem start-up time \stopitem
+ \startitem processing pages \stopitem
+ \startitem finishing the document \stopitem
+ \startitem the console used \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=Examples]
+
+\startitemize
+\startitem
+ see \goto{onandon-performance.pdf}[file(onandon-performance.pdf)] for timings
+\stopitem
+\startitem
+ you can try \type {--timing} to see where \LUA\ spends it time
+\stopitem
+\startitem
+ analyzing with \type {--profile} can give some indication (but is slow)
+\stopitem
+\stopitemize
+
+\stopchapter
+
+\stopdocument
diff --git a/doc/context/presentations/context/2017/context-2017-synctex.pdf b/doc/context/presentations/context/2017/context-2017-synctex.pdf
new file mode 100644
index 000000000..d5b39f095
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-synctex.pdf
Binary files differ
diff --git a/doc/context/presentations/context/2017/context-2017-synctex.tex b/doc/context/presentations/context/2017/context-2017-synctex.tex
new file mode 100644
index 000000000..0ca6dfd0e
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-synctex.tex
@@ -0,0 +1,102 @@
+% \setupsynctex
+% [state=start]
+
+% \enabletrackers
+% [system.synctex.visualize]
+
+\usemodule[present-lines]
+
+\definecolor[maincolor][r=.6]
+
+\startdocument[title=\SYNCTEX,subtitle={\CONTEXT\ 2017 Maibach}]
+
+\startchapter[title=What is \SYNCTEX]
+
+\startitemize
+ \startitem it is a mechanism for going back from viewer to editor \stopitem
+ \startitem it uses an extra (zipped) output file \stopitem
+ \startitem it adds an overhead of 5 to 15 percent runtime \stopitem
+ \startitem it is designed with a specific macro package in mind \stopitem
+ \startitem the rather generic approach works okay for simple document layouts \stopitem
+ \startitem but it often fails for projects that use multiple files\stopitem
+ \startitem and that moves information around like \XML\ encoded files \stopitem
+\stopitemize
+
+\startchapter[title=Disclaimer]
+
+\startitemize
+ \startitem till recently it was supported in \CONTEXT\ as-it-was \stopitem
+ \startitem there were no compliants, so it must have worked ok for most users \stopitem
+ \startitem we never used it ourselves because of mentioned reasons \stopitem
+ \startitem we only wanted to support it when it works ok in projects \stopitem
+ \startitem (think of thousands of \XML\ with deeply nested inclusions in one document) \stopitem
+ \startitem but what we support now is purely based on personal experiences \stopitem
+ \startitem we don't use it ourselves so feedback is welcome \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=What we do]
+
+\startitemize
+ \startitem the normal \SYNCTEX\ mechanism is disabled \stopitem
+ \startitem when told so, \CONTEXT\ will kick in its own code \stopitem
+ \startitem this is done by using \LUA\ code to set the right information \stopitem
+ \startitem only source files that make sense are dealt with \stopitem
+ \startitem this protects the styles from unwanted changes \stopitem
+ \startitem within reasonable bounds \XML\ is supported \stopitem
+ \startitem this also includes nested documents \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=How it works]
+
+\startitemize
+ \startitem we only mark text and don't bother about the rest \stopitem
+ \startitem we collapse information about whole stretches \stopitem
+ \startitem the extra file is therefore not that large \stopitem
+ \startitem so we can do without compression \stopitem
+ \startitem some care is needed to avoid interference with the editors parser \stopitem
+ \startitem (read: we need to get rid of the rather complex and heuristics) \stopitem
+ \startitem (read: it would be nice to have a simple robust parser option) \stopitem
+ \startitem there are flaws but I will look into them when motivated \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=What the user gets]
+
+\startitemize
+ \startitem a way to turn it on:
+ \starttyping
+ \setupsynctex[state=start]
+ \stoptyping
+ \stopitem
+ \startitem control over methods:
+ \starttyping
+ \setupsynctex[method=max]
+ \stoptyping
+ \stopitem
+ \startitem visual tracing:
+ \starttyping
+ \enabletrackers[system.synctex.visualize]
+ \stoptyping
+ \stopitem
+ \startitem some low level commands:
+ \starttyping
+ \synctexblockfilename{filename}
+ \synctexsetfilename {filename}
+ \synctexresetfilename
+ \synctexpause
+ \synctexresume
+ \stoptyping
+ \stopitem
+\stopitemize
+
+% \enabletrackers[system.synctex.xml]
+% \enabledirectives[system.synctex.details]
+
+\stopchapter
+
+\stopdocument
diff --git a/doc/context/presentations/context/2017/context-2017-tables.pdf b/doc/context/presentations/context/2017/context-2017-tables.pdf
new file mode 100644
index 000000000..299c21f39
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-tables.pdf
Binary files differ
diff --git a/doc/context/presentations/context/2017/context-2017-tables.tex b/doc/context/presentations/context/2017/context-2017-tables.tex
new file mode 100644
index 000000000..6f2822668
--- /dev/null
+++ b/doc/context/presentations/context/2017/context-2017-tables.tex
@@ -0,0 +1,175 @@
+\usemodule[present-lines]
+
+\definecolor[maincolor][r=.4,b=.4]
+
+\startdocument[title=Tables,subtitle={\CONTEXT\ 2017 Maibach}]
+
+\startchapter[title=Variants]
+
+\startitemize
+ \startitem
+ Good old \TABLE, a wrapper about \TEX's alignment.
+ \stopitem
+ \startitem
+ Running text, break across pages tabulates.
+ \stopitem
+ \startitem
+ Tables that behave like \HTML\ tables therefore called
+ natural tables.
+ \stopitem
+ \startitem
+ A variant on this that is more easy to extend, tagged
+ extreme tables.
+ \stopitem
+ \startitem
+ A low profile linetable mechanism that can span pages
+ and breaks well.
+ \stopitem
+ \startitem
+ A way to make huge tables without overflowing \TEX\ too
+ soon.
+ \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=\TABLE]
+
+\startitemize
+ \startitem
+ Based on the \TABLE\ macro package.
+ \stopitem
+ \startitem
+ Detailed control over spacing.
+ \stopitem
+ \startitem
+ Somewhat inconsistent spacing out of the box.
+ \stopitem
+ \startitem
+ Extended with extra features.
+ \stopitem
+ \startitem
+ Mostly rewritten but within the original concept.
+ \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=Tabulate]
+
+\startitemize
+ \startitem
+ Mostly meant for tables that are part of the text flow.
+ \stopitem
+ \startitem
+ Breaks paragraphs across pages.
+ \stopitem
+ \startitem
+ Sort of compatible in control with \TABLE.
+ \stopitem
+ \startitem
+ The system that I used most often.
+ \stopitem
+ \startitem
+ It uses multiple passes if needed.
+ \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=Natural tables]
+
+\startitemize
+ \startitem
+ Modelled after \HTML\ tables.
+ \stopitem
+ \startitem
+ Often used in \XML\ workflows, possibly as cals tables.
+ \stopitem
+ \startitem
+ To some extend automatic spans horizontally and vertically.
+ \stopitem
+ \startitem
+ There are a couple of (undocumented and obscure) flags that can control
+ behaviour.
+ \stopitem
+ \startitem
+ They can break cross pages if needed.
+ \stopitem
+ \startitem
+ Tables, rows and cells have framed like properties.
+ \stopitem
+ \startitem
+ Not the fastest mechanism as it used several passes (for which it
+ stores all cells).
+ \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=Extreme tables]
+
+\startitemize
+ \startitem
+ Again modelled after \HTML\ tables.
+ \stopitem
+ \startitem
+ A few less options but also some more than natural tables.
+ \stopitem
+ \startitem
+ Most work is delegated to \LUA.
+ \stopitem
+ \startitem
+ Uses buffers and therefore nesting is (as with natural tables)
+ possible but with care.
+ \stopitem
+\stopitemize
+
+\stopchapter
+
+\startchapter[title=Line tables]
+
+\startitemize
+ \startitem
+ Written for and used in a project long ago.
+ \stopitem
+ \startitem
+ Meant for huge tables that span multiple pages horizontally
+ and vertically.
+ \stopitem
+ \startitem
+ It only can have simple colored backgrounds.
+ \stopitem
+ \startitem
+ Hardly used.
+ \stopitem
+ \startitem
+ I need to redo (or check) the implementation some day.
+ \stopitem
+\stopitemize
+
+\startchapter[title=Frame tables]
+
+\startitemize
+ \startitem
+ Written for and used for Thomas who needs real huge tables
+ generated from \XML.
+ \stopitem
+ \startitem
+ It's a single pass mechanism.
+ \stopitem
+ \startitem
+ Each cell is a framed.
+ \stopitem
+ \startitem
+ Dimensions need to be adapted when you want predictable output.
+ \stopitem
+ \startitem
+ I might extend it but within reasonable bounds.
+ \stopitem
+\stopitemize
+
+\stopchapter
+
+
+\stopdocument
diff --git a/doc/context/presentations/context/2019/context-2019-lmtx.pdf b/doc/context/presentations/context/2019/context-2019-lmtx.pdf
new file mode 100644
index 000000000..7cf257347
--- /dev/null
+++ b/doc/context/presentations/context/2019/context-2019-lmtx.pdf
Binary files differ
diff --git a/doc/context/presentations/context/2019/context-2019-lmtx.tex b/doc/context/presentations/context/2019/context-2019-lmtx.tex
new file mode 100644
index 000000000..62dd915ad
--- /dev/null
+++ b/doc/context/presentations/context/2019/context-2019-lmtx.tex
@@ -0,0 +1,201 @@
+% macros=mkvi
+
+\usemodule[abbreviations-smallcaps]
+\usemodule[present-luatex]
+
+\logo [LUAMETATEX] {LuaMeta\TeX}
+
+\setupbodyfont[12pt]
+
+\setupalign[verytolerant]
+
+\setupdocument
+ [title={Lean and mean},
+ subtitle={\LUAMETATEX},
+ location={\ConTeXt\ meeting, September 2019},
+ author={Hans & Alan},
+ mp:title={\LUAMETATEX}]
+
+\startdocument
+
+\setupitemize[headintext]
+\setupitemize[headstyle=bold]
+
+\page \setupdocument[mp:subtitle={How it became}]
+
+\startitemize
+ \starthead {interferences:}
+ \CONTEXT, plain \TEX\ and \LATEX\ all have different demands (we want to
+ experiment and move on and users pick up fast)
+ \stophead
+ \starthead {complexity:}
+ the source tree is way too complex as is the build (we only need \LUATEX)
+ \stophead
+ \starthead {distributions:}
+ no one can guarantee stability for \CONTEXT\ (being a minor player but
+ often a bit ahead)
+ \stophead
+ \starthead {annoyances:}
+ experimental codes leads to usage outside \CONTEXT\ and that triggers
+ complaints
+ \stophead
+ \starthead {motivation:}
+ running into folks who love to stress \quotation {huge bugs} and
+ \quotation {much instability} wastes energy
+ \stophead
+ \starthead {arguments:}
+ I got tired of \quotation {you need to support this because \unknown}
+ blabla
+ \stophead
+ \starthead {nagging:}
+ like \quotation {the manual \unknown} is becoming too tiresome, so best
+ keep experiments within the \CONTEXT\ bubble
+ \stophead
+\stopitemize
+
+\page \setupdocument[mp:subtitle={What it is}]
+
+\startitemize
+ \starthead {simplification:}
+ we don't need all what is currently in the \LUATEX\ engine as we don't
+ use it
+ \stophead
+ \starthead {source:}
+ there is much less of it and we can get rid of \WEB\ artifacts
+ \stophead
+ \starthead {compilation:}
+ there was much more going on than was needed and only a few knew those
+ details
+ \stophead
+ \starthead {consistency:}
+ to guarantee consistency with \CONTEXT\ the source code will be part of
+ the source distribution (once I'm satisfied)
+ \stophead
+ \starthead {marketing:}
+ this way the relation with \CONTEXT\ and its user base is more clear
+ \stophead
+ \starthead {playground:}
+ we can move forward and experiment without the danger of running into
+ problems with non \CONTEXT\ users: \quotation {use it at your own risk}
+ \stophead
+ \starthead {possibilities:}
+ playing a bit more with the bits and pieces that are reponsible for most
+ (interfering) issues, like the the (asynchronous) page builder
+ \stophead
+\stopitemize
+
+\page \setupdocument[mp:subtitle={Implications}]
+
+\startitemize
+ \starthead {binary:}
+ there is only one relatively small binary needed (that does all things
+ needed)
+ \stophead
+ \starthead {code base:}
+ there comes an extra source tree, but it's small (compresses to around 2
+ MB)
+ \stophead
+ \starthead {user control:}
+ if needed users can compile the program so we're self contained
+ \stophead
+ \starthead {future safe:}
+ we can move forward and improve
+ \stophead
+ \starthead {modern:}
+ a code base with the latest \LUATEX, \MPLIB\ and \LUA
+ \stopitem
+ \starthead {side effect:}
+ we drop \LUAJIT\ as it doesn't keep up (and benefits are too small)
+ \stophead
+ \starthead {design:}
+ we have a better separation between the Knuthian front- and output format
+ driven backend
+ \stophead
+ \starthead {independent:}
+ there is no dependency on external libraries, we keep all we need in the
+ code base (we only use a few small third party libraries)
+ \stophead
+\stopitemize
+
+\page \setupdocument[mp:subtitle={A few notes}]
+
+\startitemize
+ \starthead {hobyism}
+ we don't need to carry the burden of everything (unless paid for it's
+ only fun and users that drives development)
+ \stophead
+ \starthead {convenience:}
+ the faster compilation makes reworking and experimenting reasonable
+ \stophead
+ \starthead {stepwise:}
+ I take my time an do string stepswise because things should not break
+ without fast recovery
+ \stophead
+ \starthead {feelgood:}
+ this all fits well into the good old \TEX\ extension model
+ \stophead
+ \starthead {eventually:}
+ when proven useful we can always push code upstream into \LUATEX
+ \stophead
+\stopitemize
+
+\page \setupdocument[mp:subtitle={Bits and pieces}]
+
+\startitemize
+ \starthead {original:}
+ the starting point is \LUATEX, original \WEB\ code, already \CWEB\ code
+ \stophead
+ \starthead {stability:}
+ after a initial stage \LUATEX\ was stepwise extended till version one
+ a few years ago
+ \stophead
+ \starthead {frozen:}
+ there were only a few changes after that but no real conceptual ones
+ \stophead
+ \starthead {engine:}
+ what is now called \LUAMETATEX\ is a reworked code base
+ \stophead
+ \starthead {graphics:}
+ also \MPLIB\ has been reworked a bit and some extensions were added
+ \stophead
+ \starthead {libraries:}
+ there are a few extra (small) helper libs, but all in the source tree
+ \stophead
+ \starthead {pplib:}
+ we already use the next version of pplib
+ \stophead
+ \starthead {pruning:}
+ and best of all, quite some not used code could go
+ \stophead
+\stopitemize
+
+\page \setupdocument[mp:subtitle={Some details}]
+
+\startitemize
+ \starthead {source tree:}
+ the code base has been regrouped, globals became more local (work in
+ progress), header files were added
+ \stophead
+ \starthead {source files:}
+ there is hardly any font related code, languages were kept, and the
+ backend code is dropped: show files
+ \stophead
+ \starthead {libraries:}
+ a few libs were added and dropped: show some
+ \stophead
+ \starthead {cmake:}
+ compilation is different: work in progress
+ \stophead
+ \starthead {mkxl:}
+ there are new files in \CONTEXT: \type {driv}, \type {lpdf}, \type {.mkxl}
+ and expect more
+ \stophead
+ \starthead {binary:}
+ there is only one stub for all
+ \stophead
+\stopitemize
+
+{\infofont during presentation: show the source tree as well as the binary directory}
+
+\stopdocument
+
diff --git a/doc/context/sources/general/magazines/mag-1104-mkiv.tex b/doc/context/sources/general/magazines/mag-1104-mkiv.tex
index 4cbc3d921..ef46b6fe1 100644
--- a/doc/context/sources/general/magazines/mag-1104-mkiv.tex
+++ b/doc/context/sources/general/magazines/mag-1104-mkiv.tex
@@ -497,25 +497,25 @@ Let's look at outlines.
save h ; h = 12mm ;
draw lmt_outline [
- content = "hello"
+ text = "hello"
kind = "draw",
drawcolor = "darkblue",
] ysized h ;
draw lmt_outline [
- content = "hello",
+ text = "hello",
kind = "fill",
fillcolor = "darkred",
] ysized h shifted (3.75h,0) ;
draw lmt_outline [
- content = "hello",
+ test = "hello",
kind = "both",
fillcolor = "darkred",
] ysized h shifted (7.5h,0mm) ;
draw lmt_outline [
- content = "hello",
+ text = "hello",
kind = "both",
fillcolor = "darkred",
drawcolor = "darkblue",
@@ -523,7 +523,7 @@ Let's look at outlines.
] ysized h shifted (0,-1.25h) ;
draw lmt_outline [
- content = "hello",
+ text = "hello",
kind = "reverse",
fillcolor = "darkred",
drawcolor = "darkblue",
@@ -531,8 +531,8 @@ Let's look at outlines.
] ysized h shifted (3.75h,-1.25h) ;
draw lmt_outline [
- content = "hello",
- kind = "u",
+ text = "hello",
+ kind = "fillup",
fillcolor = "darkgreen",
rulethickness = 0,
] ysized h shifted (7.5h,-1.25h) ;
@@ -552,7 +552,7 @@ outline and fill. Let's show some more:
\startbuffer
\startMPcode{doublefun}
draw lmt_outline [
- content = "\obeydiscretionaries\samplefile{tufte}",
+ text = "\obeydiscretionaries\samplefile{tufte}",
align = "normal",
kind = "draw",
drawcolor = "darkblue",
@@ -569,7 +569,7 @@ outline and fill. Let's show some more:
\startbuffer
\startMPcode{doublefun}
draw lmt_outline [
- content = "\obeydiscretionaries\samplefile{ward}",
+ text = "\obeydiscretionaries\samplefile{ward}",
align = "normal,tolerant",
width = 10cm,
kind = "draw",
@@ -587,7 +587,7 @@ outline and fill. Let's show some more:
\startbuffer
\startMPcode{doublefun}
draw lmt_outline [
- content = "\obeydiscretionaries\samplefile{sapolsky}",
+ text = "\obeydiscretionaries\samplefile{sapolsky}",
align = "normal,tolerant",
style = "bold",
width = 10cm,
@@ -611,14 +611,14 @@ That interface has evolved over time, also due to more advanced possibilities in
\startbuffer
\startMPcode{doublefun}
draw lmt_followtext [
- content = "How well does it work {\bf 1}! ",
+ text = "How well does it work {\bf 1}! ",
path = (fullcircle scaled 4cm),
trace = true,
spread = true,
] ysized 5cm ;
draw lmt_followtext [
- content = "How well does it work {\bf 2}! ",
+ text = "How well does it work {\bf 2}! ",
path = fullcircle scaled 4cm,
trace = true,
spread = false,
@@ -626,13 +626,13 @@ That interface has evolved over time, also due to more advanced possibilities in
] ysized 5cm shifted (0,-6cm) ;
draw lmt_followtext [
- content = "How well does it work {\bf 3}! ",
+ text = "How well does it work {\bf 3}! ",
trace = true,
autoscaleup = "yes"
] ysized 5cm shifted (6cm,0) ;
draw lmt_followtext [
- content = "How well does it work {\bf 4}! ",
+ text = "How well does it work {\bf 4}! ",
path = fullcircle scaled 2cm,
trace = true,
autoscaleup = "max"
diff --git a/doc/context/sources/general/manuals/lowlevel/lowlevel-boxes.tex b/doc/context/sources/general/manuals/lowlevel/lowlevel-boxes.tex
new file mode 100644
index 000000000..986d07b1b
--- /dev/null
+++ b/doc/context/sources/general/manuals/lowlevel/lowlevel-boxes.tex
@@ -0,0 +1,698 @@
+% language=us
+
+% \hfil \hss
+% spread
+
+\environment lowlevel-style
+
+\startdocument
+ [title=boxes,
+ color=middlered]
+
+\startsection[title=Preamble]
+
+\startsubsection[title=Introduction]
+
+An average \CONTEXT\ user will not use the low level box primitives but a basic
+understanding of how \TEX\ works doesn't hurt. In fact, occasionally using a box
+command might bring a solution not easily achieved otherwise, simply because a
+more high level interface can also be in the way.
+
+The best reference is of course The \TeX book so if you're really interested in
+the details you should get a copy of that book. Below I will not go into details
+about all kind of glues, kerns and penalties, just boxes it is.
+
+This explanation will be extended when I feel the need (or users have questions
+that can be answered here).
+
+\stopsubsection
+
+\startsubsection[title=Boxes]
+
+This paragraph of text is made from lines that contain words that themselves
+contain symbolic representations of characters. Each line is wrapped in a so
+called horizontal box and eventually those lines themselves get wrapped in what
+we call a vertical box.
+
+\startbuffer
+\vbox \bgroup
+ \hsize 5cm
+ \raggedright
+ This is a rather narrow paragraph blown up a bit. Here we use a flush left,
+ aka ragged right, approach.
+\egroup
+\stopbuffer
+
+When we expose some details of a paragraph it looks like this:
+
+\startlinecorrection
+\startcombination[2*1]
+ {\scale[width=8cm]{\showmakeup[boxes]\getbuffer}} {}
+ {\scale[width=8cm]{\showmakeup\getbuffer}} {}
+\stopcombination
+\stoplinecorrection
+
+The left only shows the boxes, the variant at the right shows (font) kerns and
+glue too. Because we flush left, there is rather strong right skip glue at the
+right boundary of the box. If font kerns show up depends on the font, not all
+fonts have them (or have only a few). The glyphs themselves are also kind of
+boxed, as their dimensions determine the area that they occupy:
+
+\startlinecorrection
+ \scale[width=\textwidth]{\showglyphs\hbox{This is a rather ...}}
+\stoplinecorrection
+
+But, internally they are not really boxed, as they already are a single quantity.
+The same is true for rules: they are just blobs with dimensions. A box on the
+other hand wraps a linked list of so called nodes: glyphs, kerns, glue,
+penalties, rules, boxes, etc. It is a container with properties like width,
+height, depth and shift.
+
+\stopsubsection
+
+\stopsection
+
+\startsection[title={\TEX\ primitives}]
+
+The box model is reflected in \TEX's user interface but not by that many
+commands, most noticeably \type {\hbox}, \type {\vbox} and \type {\vtop}. Here is
+an example of the first one:
+
+\starttyping[option=TEX]
+\hbox width 10cm{text}
+\hbox width 10cm height 1cm depth 5mm{text}
+text \raise5mm\hbox{text} text
+\stoptyping
+
+The \type {\raise} and \type {\lower} commands behave the same but in opposite
+directions. One could as well have been defined in terms of the other.
+
+\startbuffer
+text \raise 5mm \hbox to 2cm {text}
+text \lower -5mm \hbox to 2cm {text}
+text \raise -5mm \hbox to 2cm {text}
+text \lower 5mm \hbox to 2cm {text}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+{\dontcomplain\showboxes\getbuffer}
+\stoplinecorrection
+
+A box can be moved to the left or right but, believe it or not, in \CONTEXT\ we
+never use that feature, probably because the consequences for the width are such
+that we can as well use kerns. Here are some examples:
+
+\startbuffer
+text \vbox{\moveleft 5mm \hbox {left}}text !
+text \vbox{\moveright 5mm \hbox{right}}text !
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+{\dontcomplain\getbuffer}
+\stoplinecorrection
+
+\startbuffer
+text \vbox{\moveleft 25mm \hbox {left}}text !
+text \vbox{\moveright 25mm \hbox{right}}text !
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+{\dontcomplain\getbuffer}
+\stoplinecorrection
+
+Code like this will produce a complaint about an underfull box but we can easily
+get around that:
+
+\startbuffer
+text \raise 5mm \hbox to 2cm {\hss text}
+text \lower -5mm \hbox to 2cm {text\hss}
+text \raise -5mm \hbox to 2cm {\hss text}
+text \lower 5mm \hbox to 2cm {text\hss}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+The \type {\hss} primitive injects a glue that when needed will fill up the
+available space. So, here we force the text to the right or left.
+
+\startlinecorrection
+{\dontcomplain\showboxes\getbuffer}
+\stoplinecorrection
+
+We have three kind of boxes: \type {\hbox}, \type {\vbox} and \type {\vtop}:
+
+\startbuffer
+\hbox{\strut height and depth\strut}
+\vbox{\hsize 4cm \strut height and depth\par and width\strut}
+\vtop{\hsize 4cm \strut height and depth\par and width\strut}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+A \type {\vbox} aligns at the bottom and a \type {\vtop} at the top. I have added
+some so called struts to enforce a consistent height and depth. A strut is an
+invisible quantity (consider it a black box) that enforces consistent line
+dimensions: height and depth.
+
+
+\startlinecorrection
+{\dontcomplain\hbox{\showstruts\showboxes\getbuffer}}
+\stoplinecorrection
+
+You can store a box in a register but you need to be careful not to use a
+predefined one. If you need a lot of boxes you can reserve some for your own:
+
+\starttyping
+\newbox\MySpecialBox
+\stoptyping
+
+but normally you can do with one of the scratch registers, like 0, 2, 4, 6 or 8,
+for local boxes, and 1, 3, 5, 7 and 9 for global ones. Registers are used like:
+
+\starttyping
+ \setbox0\hbox{here}
+\global\setbox1\hbox{there}
+\stoptyping
+
+In \CONTEXT\ you can also use
+
+\starttyping
+\setbox\scratchbox \hbox{here}
+\setbox\scratchboxone\hbox{here}
+\setbox\scratchboxtwo\hbox{here}
+\stoptyping
+
+and some more. In fact, there are quite some predefined scratch registers (boxes,
+dimensions, counters, etc). Feel free to investigate further.
+
+When a box is stored, you can consult its dimensions with \type {\wd}, \type
+{\ht} and \type {\dp}. You can of course store them for later use.
+
+\starttyping
+\scratchwidth \wd\scratchbox
+\scratchheight\ht\scratchbox
+\scratchdepth \dp\scratchbox
+\scratchtotal \dimexpr\ht\scratchbox+\dp\scratchbox\relax
+\scratchtotal \htdp\scratchbox
+\stoptyping
+
+The last line is \CONTEXT\ specific. You can also set the dimensions
+
+\starttyping
+\wd\scratchbox 10cm
+\ht\scratchbox 10mm
+\dp\scratchbox 5mm
+\stoptyping
+
+So you can cheat! A box is placed with \type {\copy}, which keeps the original
+intact or \type {\box} which just inserts the box and then wipes the register. In
+practice you seldom need a copy, which is more expensive in runtime anyway. Here
+we use copy because it serves the examples.
+
+\starttyping
+\copy\scratchbox
+\box \scratchbox
+\stoptyping
+
+\stopsection
+
+\startsection[title={\ETEX\ primitives}]
+
+The \ETEX\ extensions don't add something relevant for boxes, apart from that you
+can use the expressions mechanism to mess around with their dimensions. There is
+a mechanism for typesetting r2l within a paragraph but that has limited
+capabilities and doesn't change much as it's mostly a way to trick the backend
+into outputting a stretch of text in the other direction. This feature is not
+available in \LUATEX\ because it has an alternative direction mechanism.
+
+\stopsection
+
+\startsection[title={\LUATEX\ primitives}]
+
+The concept of boxes is the same in \LUATEX\ as in its predecessors but there are
+some aspects to keep in mind. When a box is typeset this happens in \LUATEX:
+
+\startitemize[n]
+ \startitem
+ A list of nodes is constructed. In \LUATEX\ this is a double linked
+ list (so that it can easily be manipulated in \LUA) but \TEX\ itself
+ only uses the forward links.
+ \stopitem
+ \startitem
+ That list is hyphenated, that is: so called discretionary nodes are
+ injected. This depends on the language properties of the glyph
+ (character) nodes.
+ \stopitem
+ \startitem
+ Then ligatures are constructed, if the font has such combinations. When
+ this built|-|in mechanism is used, in \CONTEXT\ we speak of base mode.
+ \stopitem
+ \startitem
+ After that inter|-|character kerns are applied, if the font provides
+ them. Again this is a base mode action.
+ \stopitem
+ \startitem
+ Finally the box gets packaged:
+ \startitemize
+ \startitem
+ In the case of a horizontal box, the list is packaged in a
+ hlist node, basically one liner, and its dimensions are calculated
+ and set.
+ \stopitem
+ \startitem
+ In the case of a vertical box, the paragraph is broken into one
+ or more lines, without hyphenation, with optimal hyphenation or
+ in the worst case with so called emergency stretch applied, and
+ the result becomes a vlist node with its dimensions set.
+ \stopitem
+ \stopitemize
+ \stopitem
+\stopitemize
+
+In traditional \TEX\ the first four steps are interwoven but in \LUATEX\ we need
+them split because the step~5 can be overloaded by a callback. In that case steps
+3 and 4 (and maybe 2) are probably also overloaded, especially when you bring
+handling of fonts under \LUA\ control.
+
+New in \LUATEX\ are three packers: \type {\hpack}, \type {\vpack} and \type
+{\tpack}, which are companions to \type {\hbox}, \type {\vbox} and \type {\vtop}
+but without the callbacks applied. Using them is a bit tricky as you never know
+if a callback should be applied, which, because users can often add their own
+\LUA\ code, is not something predictable.
+
+Another box related extension is direction. There are four possible directions
+but because in \LUAMETATEX\ there are only two. Because this model has been upgraded,
+it will be discusses in the next section. A \CONTEXT\ user is supposed to use the
+official \CONTEXT\ interfaces in order to be downward compatible.
+
+\stopsection
+
+\startsection[title={\LUAMETATEX\ primitives}]
+
+There are two possible directions: left to right (the default) and right to left
+for Hebrew and Arabic. Here is an example that shows how it'd done with low level
+directives:
+
+\startbuffer
+\hbox direction 0 {from left to right}
+\hbox direction 1 {from right to left}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+A low level direction switch is done with:
+
+\startbuffer
+\hbox direction 0
+ {from left to right \textdirection 1 from right to left}
+\hbox direction 1
+ {from right to left \textdirection 1 from left to right}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+but actually this is kind of {\em not done} in \CONTEXT, because there you are
+supposed to use the proper direction switches:
+
+\startbuffer
+\naturalhbox {from left to right}
+\reversehbox {from right to left}
+\naturalhbox {from left to right \righttoleft from right to left}
+\reversehbox {from right to left \lefttoright from left to right}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+Often more is needed to properly support right to left typesetting so using the
+\CONTEXT\ commands is more robust.
+
+In \LUAMETATEX\ the box model has been extended a bit, this as a consequence of
+dropping the vertical directional typesetting, which never worked well. In
+previous sections we discussed the properties width, height and depth and the
+shift resulting from a \type {\raise}, \type {\lower}, \type {\moveleft} and
+\type {\moveright}. Actually, the shift is also used in for instance positioning
+math elements.
+
+The way shifting influences dimensions can be somewhat puzzling. Internally, when
+\TEX\ packages content in a box there are two cases:
+
+\startitemize
+ \startitem
+ When a horizontal box is made, and \typ {height - shift} is larger than the
+ maximum height so far, that delta is taken. When \typ {depth + shift} is
+ larger than the current depth, then that depth is adapted. So, a shift up
+ influences the height and a shift down influences the depth.
+ \stopitem
+ \startitem
+ In the case of vertical packaging, when \typ {width + shift} is larger
+ than the maximum box (line) width so far, that maximum gets bumped. So, a
+ shift to the right can contribute, but a shift to the left cannot result
+ in a negative width. This is also why vertical typesetting, where height
+ and depth are swapped with width, goes wrong: we somehow need to map two
+ properties onto one and conceptually \TEX\ is really set up for
+ horizontal typesetting. (And it's why I decided to just remove it from the
+ engine.)
+ \stopitem
+\stopitemize
+
+This is one of these cases where \TEX\ behaves as expected but it also means that
+there is some limitation to what can be manipulated. Setting the shift using one
+of the four commands has a direct consequence when a box gets packaged which
+happens immediately because the box is an argument to the foursome.
+
+There is in traditional \TEX, probably for good reason, no way to set the shift
+of a box, if only because the effect would normally be none. But in \LUATEX\ we
+can cheat, and therefore, for educational purposed \CONTEXT\ has implements
+some cheats.
+
+We use this sample box:
+
+\startbuffer[demo]
+\setbox\scratchbox\hbox\bgroup
+ \middlegray\vrule width 20mm depth -.5mm height 10mm
+ \hskip-20mm
+ \darkgray \vrule width 20mm height -.5mm depth 5mm
+\egroup
+\stopbuffer
+
+\typebuffer[demo][option=TEX]
+
+When we mess with the shift using the \CONTEXT\ \type {\shiftbox} helper, we see
+no immediate effect. We only get the shift applied when we use another helper,
+\type {\hpackbox}.
+
+\startbuffer
+\hbox\bgroup
+ \showstruts \strut
+ \quad \copy\scratchbox
+ \quad \shiftbox\scratchbox -20mm \copy\scratchbox
+ \quad \hpackbox\scratchbox \box \scratchbox
+ \quad \strut
+\egroup
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\getbuffer[demo]\getbuffer
+\stoplinecorrection
+
+When instead we use \type {\vpackbox} we get a different result. This time we
+move left.
+
+\startbuffer
+\hbox\bgroup
+ \showstruts \strut
+ \quad \copy\scratchbox
+ \quad \shiftbox\scratchbox -10mm \copy\scratchbox
+ \quad \vpackbox\scratchbox \copy\scratchbox
+ \quad \strut
+\egroup
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\getbuffer[demo]\getbuffer
+\stoplinecorrection
+
+The shift is set via \LUA\ and the repackaging is also done in \LUA, using the
+low level \type {hpack} and \type {vpack} helpers and these just happen to look
+at the shift when doing their job. At the \TEX\ end this never happens.
+
+This long exploration of shifting serves a purpose: it demonstrates that there is
+not that much direct control over boxes apart from their three dimensions.
+However this was never a real problem as one can just wrap a box in another one
+and use kerns to move the embedded box around. But nevertheless I decided to see
+if the engine can be a bit more helpful, if only because all that extra wrapping
+gives some overhead and complications when we want to manipulate boxes. And of
+course it is also a nice playground.
+
+We start with changing the direction. Changing this property doesn't require
+repackaging because directions are not really dealt with in the frontend. When
+a box is converted to (for instance \PDF) the reversion happens.
+
+\startbuffer
+\setbox\scratchbox\hbox{whatever}
+\the\boxdirection\scratchbox: \copy\scratchbox \crlf
+\boxdirection\scratchbox 1
+\the\boxdirection\scratchbox: \copy\scratchbox
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+Another property that can be queried and set is an attribute. In order to get
+a private attribute we define one.
+
+\startbuffer
+\newattribute\MyAt
+\setbox\scratchbox\hbox attr \MyAt 123 {whatever}
+[\the\boxattr\scratchbox\MyAt]
+\boxattr\scratchbox\MyAt 456
+[\the\boxattr\scratchbox\MyAt]
+[\ifnum\boxattr\scratchbox\MyAt>400 okay\fi]
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+The sum of the height and depth is available too. Because for practical reasons
+setting that property is also needed then, the choice was made to distribute the
+value equally over height and depth.
+
+\startbuffer
+\setbox\scratchbox\hbox {height and depth}
+[\the\ht\scratchbox]
+[\the\dp\scratchbox]
+[\the\boxtotal\scratchbox]
+\boxtotal\scratchbox=20pt
+[\the\ht\scratchbox]
+[\the\dp\scratchbox]
+[\the\boxtotal\scratchbox]
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+We've now arrived to a set of properties that relate to each other. They are
+a bit complex and given the number of possibilities one might need to revert
+to some trial and error: orientations and offsets. As with the dimensions,
+directions and attributes, they are passed as box specification. We start
+with the orientation.
+
+\startbuffer
+\hbox \bgroup \showboxes
+ \hbox orientation 0 {right}
+ \quad \hbox orientation 1 {up}
+ \quad \hbox orientation 2 {left}
+ \quad \hbox orientation 3 {down}
+\egroup
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+When the orientation is set, you can also set an offset. Where shifting around a box
+can have consequences for the dimensions, an offset is virtual. It gets effective
+in the backend, when the contents is converted to some output format.
+
+\startbuffer
+\hbox \bgroup \showboxes
+ \hbox orientation 0 yoffset 10pt {right}
+ \quad \hbox orientation 1 xoffset 10pt {up}
+ \quad \hbox orientation 2 yoffset -10pt {left}
+ \quad \hbox orientation 3 xoffset -10pt {down}
+\egroup
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+The reason that offsets are related to orientation is that we need to know in
+what direction the offsets have to be applied and this binding forces the user to
+think about it. You can also set the offsets using commands.
+
+\startbuffer
+\setbox\scratchbox\hbox{whatever}%
+1 \copy\scratchbox
+2 \boxorientation\scratchbox 2 \copy\scratchbox
+3 \boxxoffset \scratchbox -15pt \copy\scratchbox
+4 \boxyoffset \scratchbox -15pt \copy\scratchbox
+5
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\ruledhbox{\getbuffer}
+\stoplinecorrection
+
+\startbuffer
+\setbox\scratchboxone\hbox{whatever}%
+\setbox\scratchboxtwo\hbox{whatever}%
+1 \boxxoffset \scratchboxone -15pt \copy\scratchboxone
+2 \boxyoffset \scratchboxone -15pt \copy\scratchboxone
+3 \boxxoffset \scratchboxone -15pt \copy\scratchboxone
+4 \boxyoffset \scratchboxone -15pt \copy\scratchboxone
+5 \boxxmove \scratchboxtwo -15pt \copy\scratchboxtwo
+6 \boxymove \scratchboxtwo -15pt \copy\scratchboxtwo
+7 \boxxmove \scratchboxtwo -15pt \copy\scratchboxtwo
+8 \boxymove \scratchboxtwo -15pt \copy\scratchboxtwo
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\ruledhbox{\getbuffer}
+\stoplinecorrection
+
+The move commands are provides as convenience and contrary to the offsets they do
+adapt the dimensions. Internally, with the box, we register the orientation and
+the offsets and when you apply these commands multiple times the current values
+get overwritten. But \unknown\ because an orientation can be more complex you
+might not get the effects you expect when the options we discuss next are used.
+The reason is that we store the original dimensions too and these come into play
+when these other options are used: anchoring. So, normally you will apply an
+orientation and offsets once only.
+
+% the next bit is derived from the followingup document
+
+The orientation specifier is actually a three byte number that best can be seen
+hexadecimal (although we stay within the decimal domain). There are three
+components: x|-|anchoring, y|-|anchoring and orientation:
+
+\starttyping
+0x<X><Y><O>
+\stoptyping
+
+or in \TEX\ speak:
+
+\starttyping
+"<X><Y><O>
+\stoptyping
+
+The landscape and seascape variants both sit on top of the baseline while the
+flipped variant has its depth swapped with the height. Although this would be
+enough a bit more control is possible.
+
+The vertical options of the horizontal variants anchor on the baseline, lower
+corner, upper corner or center.
+
+\startbuffer
+\ruledhbox orientation "002 {\TEX} and
+\ruledhbox orientation "012 {\TEX} and
+\ruledhbox orientation "022 {\TEX} and
+\ruledhbox orientation "032 {\TEX}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\ruledhbox{\getbuffer}
+\stoplinecorrection
+
+The horizontal options of the horizontal variants anchor in the center, left,
+right, halfway left and halfway right.
+
+\startbuffer
+\ruledhbox orientation "002 {\TEX} and
+\ruledhbox orientation "102 {\TEX} and
+\ruledhbox orientation "202 {\TEX} and
+\ruledhbox orientation "302 {\TEX} and
+\ruledhbox orientation "402 {\TEX}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\ruledhbox{\getbuffer}
+\stoplinecorrection
+
+The orientation has consequences for the dimensions so they are dealt with in the
+expected way in constructing lines, paragraphs and pages, but the anchoring is
+virtual, like the offsets. There are two extra variants for orientation zero: on
+top of baseline or below, with dimensions taken into account.
+
+\startbuffer
+\ruledhbox orientation "000 {\TEX} and
+\ruledhbox orientation "004 {\TEX} and
+\ruledhbox orientation "005 {\TEX}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\ruledhbox{\getbuffer}
+\stoplinecorrection
+
+The anchoring can look somewhat confusing but you need to keep in mind that it is
+normally only used in very controlled circumstances and not in running text.
+Wrapped in macros users don't see the details. We're talking boxes here, so for
+instance:
+
+\startbuffer
+test\quad
+\hbox orientation 3 \bgroup
+ \strut test\hbox orientation "002 \bgroup\strut test\egroup test%
+\egroup \quad
+\hbox orientation 3 \bgroup
+ \strut test\hbox orientation "002 \bgroup\strut test\egroup test%
+\egroup \quad
+\hbox orientation 3 \bgroup
+ \strut test\hbox orientation "012 \bgroup\strut test\egroup test%
+\egroup \quad
+\hbox orientation 3 \bgroup
+ \strut test\hbox orientation "022 \bgroup\strut test\egroup test%
+\egroup \quad
+\hbox orientation 3 \bgroup
+ \strut test\hbox orientation "032 \bgroup\strut test\egroup test%
+\egroup \quad
+\hbox orientation 3 \bgroup
+ \strut test\hbox orientation "042 \bgroup\strut test\egroup test%
+\egroup
+\quad test
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\ruledhbox{\getbuffer}
+\stoplinecorrection
+
+\stopsection
+
+\stopdocument
diff --git a/doc/context/sources/general/manuals/lowlevel/lowlevel-conditionals.tex b/doc/context/sources/general/manuals/lowlevel/lowlevel-conditionals.tex
new file mode 100644
index 000000000..ea3c9e1a2
--- /dev/null
+++ b/doc/context/sources/general/manuals/lowlevel/lowlevel-conditionals.tex
@@ -0,0 +1,1409 @@
+% language=us
+
+\environment lowlevel-style
+
+\startdocument
+ [title=conditionals,
+ color=middleblue]
+
+\startsection[title=Preamble]
+
+\startsubsection[title=Introduction]
+
+You seldom need the low level conditionals because there are quite some so called
+support macros available in \CONTEXT . For instance, when you want to compare two
+values (or more accurate: sequences of tokens), you can do this:
+
+\starttyping[option=TEX]
+\doifelse {foo} {bar} {
+ the same
+} {
+ different
+}
+\stoptyping
+
+But if you look in the \CONTEXT\ code, you will see that often we use primitives
+that start with \type {\if} in low level macros. There are good reasons for this.
+First of all, it looks familiar when you also code in other languages. Another
+reason is performance but that is only true in cases where the snippet of code is
+expanded very often, because \TEX\ is already pretty fast. Using low level \TEX\
+can also be more verbose, which is not always nice in a document source. But, the
+most important reason (for me) is the layout of the code. I often let the look
+and feel of code determine the kind of coding. This also relates to the syntax
+highlighting that I am using, which is consistent for \TEX, \METAPOST, \LUA,
+etc.\ and evolved over decades. If code looks bad, it probably is bad. Of course
+this doesn't mean all my code looks good; you're warned. In general we can say
+that I often use \type {\if...} when coding core macros, and \type {\doifelse...}
+macros in (document) styles and modules.
+
+In the sections below I will discuss the low level conditions in \TEX. For the
+often more convenient \CONTEXT\ wrappers you can consult the source of the system
+and support modules, the wiki and|/|or manuals.
+
+Some of the primitives shown here are only available in \LUATEX, and some only in
+\LUAMETATEX . We could do without them for decades but they were added to these
+engines because of convenience and, more important, because then made for nicer
+code. Of course there's also the fun aspect. This manual is not an invitation to
+use these very low level primitives in your document source. The ones that
+probably make most sense are \type {\ifnum}, \type {\ifdim} and \type {\ifcase}.
+The others are often wrapped into support macros that are more convenient.
+
+In due time I might add more examples and explanations. Also, maybe some more
+tests will show up as part of the \LUAMETATEX\ project.
+
+\stopsubsection
+
+\startsubsection[title={Number and dimensions}]
+
+Numbers and dimensions are basic data types in \TEX. When you enter one, a number
+is just that but a dimension gets a unit. Compare:
+
+\starttyping[option=TEX]
+1234
+1234pt
+\stoptyping
+
+If you also use \METAPOST, you need to be aware of the fact that in that language
+there are not really dimensions. The \type {post} part of the name implies that
+eventually a number becomes a \POSTSCRIPT\ unit which represents a base point (\type
+{bp}) in \TEX. When in \METAPOST\ you entry \type {1234pt} you actually multiply
+\type {1234} by the variable \type {pt}. In \TEX\ on the other hand, a unit like
+\type {pt} is one of the keywords that gets parsed. Internally dimensions are
+also numbers and the unit (keyword) tells the scanner what multiplier to use.
+When that multiplier is one, we're talking of scaled points, with the unit \type
+{sp}.
+
+\startbuffer
+\the\dimexpr 12.34pt \relax
+\the\dimexpr 12.34sp \relax
+\the\dimexpr 12.99sp \relax
+\the\dimexpr 1234sp \relax
+\the\numexpr 1234 \relax
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlines \getbuffer \stoplines
+
+When we serialize a dimension it always shows the dimension in points, unless we
+serialize it as number.
+
+\startbuffer
+\scratchdimen1234sp
+\number\scratchdimen
+\the\scratchdimen
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlines \getbuffer \stoplines
+
+When a number is scanned, the first thing that is taken care of is the sign. In many
+cases, when \TEX\ scans for something specific it will ignore spaces. It will
+happily accept multiple signs:
+
+\startbuffer
+\number +123
+\number +++123
+\number + + + 123
+\number +-+-+123
+\number --123
+\number ---123
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlines \getbuffer \stoplines
+
+Watch how the negation accumulates. The scanner can handle decimal, hexadecimal
+and octal numbers:
+
+\startbuffer
+\number -123
+\number -"123
+\number -'123
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlines \getbuffer \stoplines
+
+A dimension is scanned like a number but this time the scanner checks for upto
+three parts: an either or not signed number, a period and a fraction. Here no
+number means zero, so the next is valid:
+
+\startbuffer
+\the\dimexpr . pt \relax
+\the\dimexpr 1. pt \relax
+\the\dimexpr .1pt \relax
+\the\dimexpr 1.1pt \relax
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlines \getbuffer \stoplines
+
+Again we can use hexadecimal and octal numbers but when these are entered, there
+can be no fractional part.
+
+\startbuffer
+\the\dimexpr 16 pt \relax
+\the\dimexpr "10 pt \relax
+\the\dimexpr '20 pt \relax
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlines \getbuffer \stoplines
+
+The reason for discussing numbers and dimensions here is that there are cases where
+when \TEX\ expects a number it will also accept a dimension. It is good to know that
+for instance a macro defined with \type {\chardef} or \type {\mathchardef} also is
+treated as a number. Even normal characters can be numbers, when prefixed by a \type
+{`} (backtick).
+
+The maximum number in \TEX\ is 2147483647 so we can do this:
+
+\starttyping[option=TEX]
+\scratchcounter2147483647
+\stoptyping
+
+but not this
+
+\starttyping[option=TEX]
+\scratchcounter2147483648
+\stoptyping
+
+as it will trigger an error. A dimension can be positive and negative so there we
+can do at most:
+
+\starttyping[option=TEX]
+\scratchdimen 1073741823sp
+\stoptyping
+
+\startbuffer
+\scratchdimen1073741823sp
+\number\scratchdimen
+\the\scratchdimen
+\scratchdimen16383.99998pt
+\number\scratchdimen
+\the\scratchdimen
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlines
+\getbuffer
+\stoplines
+
+We can also do this:
+
+\startbuffer
+\scratchdimen16383.99999pt
+\number\scratchdimen
+\the\scratchdimen
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlines
+\getbuffer
+\stoplines
+
+but the next one will fail:
+
+\starttyping[option=TEX]
+\scratchdimen16383.9999999pt
+\stoptyping
+
+Just keep in mind that \TEX\ scans both parts as number so the error comes from
+checking if those numbers combine well.
+
+\startbuffer
+\ifdim 16383.99999 pt = 16383.99998 pt the same \else different \fi
+\ifdim 16383.999979 pt = 16383.999980 pt the same \else different \fi
+\ifdim 16383.999987 pt = 16383.999991 pt the same \else different \fi
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+Watch the difference in dividing, the \type {/} rounds, while the \type {:}
+truncates.
+
+\startlines
+\getbuffer
+\stoplines
+
+You need to be aware of border cases, although in practice they never really
+are a problem:
+
+\startbuffer
+\ifdim \dimexpr16383.99997 pt/2\relax = \dimexpr 16383.99998 pt/2\relax
+ the same \else different
+\fi
+\ifdim \dimexpr16383.99997 pt:2\relax = \dimexpr 16383.99998 pt:2\relax
+ the same \else different
+\fi
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlines
+\getbuffer
+\stoplines
+
+\startbuffer
+\ifdim \dimexpr1.99997 pt/2\relax = \dimexpr 1.99998 pt/2\relax
+ the same \else different
+\fi
+\ifdim \dimexpr1.99997 pt:2\relax = \dimexpr 1.99998 pt:2\relax
+ the same \else different
+\fi
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlines
+\getbuffer
+\stoplines
+
+\startbuffer
+\ifdim \dimexpr1.999999 pt/2\relax = \dimexpr 1.9999995 pt/2\relax
+ the same \else different
+\fi
+\ifdim \dimexpr1.999999 pt:2\relax = \dimexpr 1.9999995 pt:2\relax
+ the same \else different
+\fi
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlines
+\getbuffer
+\stoplines
+
+This last case demonstrates that at some point the digits get dropped (still
+assuming that the fraction is within the maximum permitted) so these numbers then
+are the same. Anyway, this is not different in other programming languages and
+just something you need to be aware of.
+
+\stopsubsection
+
+\stopsection
+
+\startsection[title={\TEX\ primitives}]
+
+\startsubsection[title={\tex{if}}]
+
+I seldom use this one. Internally \TEX\ stores (and thinks) in terms of tokens.
+If you see for instance \type {\def} or \type {\dimen} or \type {\hbox} these all
+become tokens. But characters like \type {A} or {@} also become tokens. In this
+test primitive all non|-|characters are considered to be the same. In the next
+examples this is demonstrated.
+
+\startbuffer
+[\if AB yes\else nop\fi]
+[\if AA yes\else nop\fi]
+[\if CDyes\else nop\fi]
+[\if CCyes\else nop\fi]
+[\if\dimen\font yes\else nop\fi]
+[\if\dimen\font yes\else nop\fi]
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+Watch how spaces after the two characters are kept: \inlinebuffer . This primitive looks
+at the next two tokens but when doing so it expands. Just look at the following:
+
+\startbuffer
+\def\AA{AA}%
+\def\AB{AB}%
+[\if\AA yes\else nop\fi]
+[\if\AB yes\else nop\fi]
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+We get: \inlinebuffer .
+
+% protected macros
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifcat}}]
+
+In \TEX\ characters (in the input) get interpreted according to their so called
+catcodes. The most common are letters (alphabetic) and and other (symbols) but
+for instance the backslash has the property that it starts a command, the dollar
+signs trigger math mode, while the curly braced deal with grouping. If for
+instance either or not the ampersand is special (for instance as column separator
+in tables) depends on the macro package.
+
+\startbuffer
+[\ifcat AB yes\else nop\fi]
+[\ifcat AA yes\else nop\fi]
+[\ifcat CDyes\else nop\fi]
+[\ifcat CCyes\else nop\fi]
+[\ifcat C1yes\else nop\fi]
+[\ifcat\dimen\font yes\else nop\fi]
+[\ifcat\dimen\font yes\else nop\fi]
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+This time we also compare a letter with a number: \inlinebuffer . In that case
+the category codes differ (letter vs other) but in this test comparing the
+letters result in a match. This is a test that is used only once in \CONTEXT\ and
+even that occasion is dubious and will go away.
+
+You can use \type {\noexpand} to prevent expansion:
+
+\startbuffer
+\def\A{A}%
+\let\B B%
+\def\C{D}%
+\let\D D%
+[\ifcat\noexpand\A Ayes\else nop\fi]
+[\ifcat\noexpand\B Byes\else nop\fi]
+[\ifcat\noexpand\C Cyes\else nop\fi]
+[\ifcat\noexpand\C Dyes\else nop\fi]
+[\ifcat\noexpand\D Dyes\else nop\fi]
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+We get: \inlinebuffer, so who still thinks that \TEX\ is easy to understand for a
+novice user?
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifnum}}]
+
+This condition compares its argument with another one, separated by an \type {<},
+\type {=} or \type {>} character.
+
+\starttyping[option=TEX]
+\ifnum\scratchcounter<0
+ less than
+\else\ifnum\scratchcounter>0
+ more than
+\else
+ equal to
+\fi zero
+\stoptyping
+
+This is one of these situations where a dimension can be used instead. In that
+case the dimension is in scaled points.
+
+\starttyping[option=TEX]
+\ifnum\scratchdimen<0
+ less than
+\else\ifnum\scratchdimen>0
+ more than
+\else
+ equal to
+\fi zero
+\stoptyping
+
+Of course this equal treatment of a dimension and number is only true when the
+dimension is a register or box property.
+
+\stopsubsection
+
+\startsection[title={\tex{ifdim}}]
+
+This condition compares one dimension with another one, separated by an \type {<},
+\type {=} or \type {>} sign.
+
+\starttyping[option=TEX]
+\ifdim\scratchdimen<0pt
+ less than
+\else\ifdim\scratchdimen>0pt
+ more than
+\else
+ equal to
+\fi zero
+\stoptyping
+
+While when comparing numbers a dimension is a valid quantity but here you cannot
+mix them: something with a unit is expected.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifodd}}]
+
+This one can come in handy, although in \CONTEXT\ it is only used in checking for
+an odd of even page number.
+
+\startbuffer
+\scratchdimen 3sp
+\scratchcounter4
+
+\ifodd\scratchdimen very \else not so \fi odd
+\ifodd\scratchcounter very \else not so \fi odd
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+As with the previously discussed \type {\ifnum} you can use a dimension variable
+too, which is then interpreted as representing scaled points. Here we get:
+
+\startlines
+\getbuffer
+\stoplines
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifvmode}}]
+
+This is a rather trivial check. It takes no arguments and just is true when we're
+in vertical mode. Here is an example:
+
+\startbuffer
+\hbox{\ifvmode\else\par\fi\ifvmode v\else h\fi mode}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+We're always in horizontal mode and issuing a \type {\par} inside a horizontal
+box doesn't change that, so we get: \ruledhbox{\inlinebuffer}.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifhmode}}]
+
+As with \type {\ifvmode} this one has no argument and just tells if we're in
+vertical mode.
+
+\startbuffer
+\vbox {
+ \noindent \ifhmode h\else v\fi mode
+ \par
+ \ifhmode h\else \noindent v\fi mode
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+You can use it for instance to trigger injection of code, or prevent that some
+content (or command) is done more than once:
+
+\startlinecorrection
+\ruledhbox{\inlinebuffer}
+\stoplinecorrection
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifmmode}}]
+
+Math is something very \TEX\ so naturally you can check if you're in math mode.
+here is an example of using this test:
+
+\starttyping[option=TEX]
+\def\enforcemath#1{\ifmmode#1\else$ #1 $\fi}
+\stoptyping
+
+Of course in reality macros that do such things are more advanced than this one.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifinner}}]
+
+\startbuffer
+\def\ShowMode
+ {\ifhmode \ifinner inner \fi hmode
+ \else\ifvmode \ifinner inner \fi vmode
+ \else\ifmmode \ifinner inner \fi mmode
+ \else \ifinner inner \fi unset
+ \fi\fi\fi}
+\stopbuffer
+
+\typebuffer[option=TEX] \getbuffer
+
+\startbuffer
+\ShowMode \ShowMode
+
+\vbox{\ShowMode}
+
+\hbox{\ShowMode}
+
+$\ShowMode$
+
+$$\ShowMode$$
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+The first line has two tests, where the first one changes the mode to horizontal
+simply because a text has been typeset. Watch how display math is not inner.
+
+\startpacked
+\startlines
+\getbuffer
+\stoplines
+\stoppacked
+
+By the way, moving the \type {\ifinner} test outside the branches (to the top of
+the macro) won't work because once the word \type {inner} is typeset we're no
+longer in vertical mode, if we were at all.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifvoid}}]
+
+A box is one of the basic concepts in \TEX. In order to understand this primitive
+we present four cases:
+
+\startbuffer
+\setbox0\hbox{} \ifvoid0 void \else content \fi
+\setbox0\hbox{123} \ifvoid0 void \else content \fi
+\setbox0\hbox{} \box0 \ifvoid0 void \else content \fi
+\setbox0\hbox to 10pt{} \ifvoid0 void \else content \fi
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+In the first case, we have a box which is empty but it's not void. It helps to
+know that internally an hbox is actually an object with a pointer to a linked
+list of nodes. So, the first two can be seen as:
+
+\starttyping
+hlist -> [nothing]
+hlist -> 1 -> 2 -> 3 -> [nothing]
+\stoptyping
+
+but in any case there is a hlist. The third case puts something in a hlist but
+then flushes it. Now we have not even the hlist any more; the box register has
+become void. The last case is a variant on the first. It is an empty box with a
+given width. The outcome of the four lines (with a box flushed in between) is:
+
+\startlines
+\getbuffer
+\stoplines
+
+So, when you want to test if a box is really empty, you need to test also its
+dimensions, which can be up to three tests, depending on your needs.
+
+\startbuffer
+\setbox0\emptybox \ifvoid0 void\else content\fi
+\setbox0\emptybox \wd0=10pt \ifvoid0 void\else content\fi
+\setbox0\hbox to 10pt {} \ifvoid0 void\else content\fi
+\setbox0\hbox {} \wd0=10pt \ifvoid0 void\else content\fi
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+Setting a dimension of a void voix (empty) box doesn't make it less void:
+
+\startlines
+\getbuffer
+\stoplines
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifhbox}}]
+
+This test takes a box number and gives true when it is an hbox.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifvbox}}]
+
+This test takes a box number and gives true when it is an vbox. Both a \type
+{\vbox} and \type {\vtop} are vboxes, the difference is in the height and depth
+and the baseline. In a \type {\vbox} the last line determines the baseline
+
+\startlinecorrection
+\ruledvbox{vbox or vtop\par vtop or vbox}
+\stoplinecorrection
+
+And in a \type {\vtop} the first line takes control:
+
+\startlinecorrection
+\ruledvtop{vbox or vtop\par vtop or vbox}
+\stoplinecorrection
+
+but, once wrapped, both internally are just vlists.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifx}}]
+
+This test is actually used a lot in \CONTEXT: it compares two token(list)s:
+
+\startbuffer
+ \ifx a b Y\else N\fi
+ \ifx ab Y\else N\fi
+\def\A {a}\def\B{b}\ifx \A\B Y\else N\fi
+\def\A{aa}\def\B{a}\ifx \A\B Y\else N\fi
+\def\A {a}\def\B{a}\ifx \A\B Y\else N\fi
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+Here the result is: \quotation{\inlinebuffer}. It does not expand the content, if
+you want that you need to use an \type {\edef} to create two (temporary) macros
+that get compared, like in:
+
+\starttyping[option=TEX]
+\edef\TempA{...}\edef\TempB{...}\ifx\TempA\TempB ...\else ...\fi
+\stoptyping
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifeof}}]
+
+This test checks if a the pointer in a given input channel has reached its end.
+It is also true when the file is not present. The argument is a number which
+relates to the \type {\openin} primitive that is used to open files for reading.
+
+\stopsubsection
+
+\startsubsection[title={\tex{iftrue}}]
+
+It does what it says: always true.
+
+\stopsubsection
+
+\startsubsection[title={\tex{iffalse}}]
+
+It does what it says: always false.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifcase}}]
+
+The general layout of an \type {\ifcase} tests is as follows:
+
+\starttyping[option=TEX]
+\ifcase<number>
+ when zero
+\or
+ when one
+\or
+ when two
+\or
+ ...
+\else
+ when something else
+\fi
+\stoptyping
+
+As in other places a number is a sequence of signs followed by one of more digits
+
+\stopsubsection
+
+\stopsection
+
+\startsection[title={\ETEX\ primitives}]
+
+\startsubsection[title={\tex{ifdefined}}]
+
+This primitive was introduced for checking the existence of a macro (or primitive)
+and with good reason. Say that you want to know if \type {\MyMacro} is defined? One
+way to do that is:
+
+\startbuffer
+\ifx\MyMacro\undefined
+ {\bf undefined indeed}
+\fi
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+This results in: \inlinebuffer , but is this macro really undefined? When \TEX\
+scans your source and sees a the escape character (the forward slash) it will
+grab the next characters and construct a control sequence from it. Then it finds
+out that there is nothing with that name and it will create a hash entry for a
+macro with that name but with no meaning. Because \type {\undefined} is also not
+defined, these two macros have the same meaning and therefore the \type {\ifx} is
+true. Imagine that you do this many times, with different macro names, then your
+hash can fill up. Also, when a user defined \type {\undefined} you're suddenly
+get a different outcome.
+
+In order to catch the last problem there is the option to test directly:
+
+\startbuffer
+\ifdefined\MyOtherMacro \else
+ {\bf also undefined}
+\fi
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+This (or course) results in: \inlinebuffer, but the macro is still sort of
+defined (with no meaning). The next section shows how to get around this.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifcsname}}]
+
+A macro is often defined using a ready made name, as in:
+
+\starttyping[option=TEX]
+\def\OhYes{yes}
+\stoptyping
+
+The name is made from characters with catcode letter which means that you cannot
+use for instance digits or underscores unless you also give these characters that
+catcode, which is not that handy in a document. You can however use \type
+{\csname} to define a control sequence with any character in the name, like:
+
+\starttyping[option=TEX]
+\expandafter\def\csname Oh Yes : 1\endcsname{yes}
+\stoptyping
+
+Later on you can get this one with \type {\csname}:
+
+\starttyping[option=TEX]
+\csname Oh Yes : 1\endcsname
+\stoptyping
+
+However, if you say:
+
+\starttyping[option=TEX]
+\csname Oh Yes : 2\endcsname
+\stoptyping
+
+you won't get some result, nor a message about an undefined control sequence, but
+the name triggers a define anyway, this time not with no meaning (undefined) but
+as equivalent to \type {\relax}, which is why
+
+\starttyping[option=TEX]
+\expandafter\ifx\csname Oh Yes : 2\endcsname\relax
+ {\bf relaxed indeed}
+\fi
+\stoptyping
+
+is the way to test its existence. As with the test in the previous section,
+this can deplete the hash when you do lots of such tests. The way out of this
+is:
+
+\starttyping[option=TEX]
+\ifcsname Oh Yes : 2\endcsname \else
+ {\bf unknown indeed}
+\fi
+\stoptyping
+
+This time there is no hash entry created and therefore there is not even an
+undefined control sequence.
+
+In \LUATEX\ there is an option to return false in case of a messy expansion
+during this test, and in \LUAMETATEX\ that is default. This means that tests can
+be made quite robust as it is pretty safe to assume that names that make sense
+are constructed from regular characters and not boxes, font switches, etc.
+
+\stopsubsection
+
+\startsubsection[title={\tex{iffontchar}}]
+
+This test was also part of the \ETEX\ extensions and it can be used to see if
+a font has a character.
+
+\startbuffer
+\iffontchar\font`A
+ {\em This font has an A!}
+\fi
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+And, as expected, the outcome is: \quotation {\inlinebuffer}. The test takes two
+arguments, the first being a font identifier and the second a character number,
+so the next checks are all valid:
+
+\starttyping[option=TEX]
+\iffontchar\font `A yes\else nop\fi\par
+\iffontchar\nullfont `A yes\else nop\fi\par
+\iffontchar\textfont0`A yes\else nop\fi\par
+\stoptyping
+
+In the perspective of \LUAMETATEX\ I considered also supporting \type {\fontid}
+but it got a bit messy due to the fact that this primitive expands in a different
+way so this extension was rejected.
+
+\stopsubsection
+
+\startsubsection[title={\tex{unless}}]
+
+You can negate the results of a test by using the \type {\unless} prefix, so for
+instance you can replace:
+
+\starttyping[option=TEX]
+\ifdim\scratchdimen=10pt
+ \dosomething
+\else\ifdim\scratchdimen<10pt
+ \dosomething
+\fi\fi
+\stoptyping
+
+by:
+
+\starttyping[option=TEX]
+\unless\ifdim\scratchdimen>10pt
+ \dosomething
+\fi
+\stoptyping
+
+\stopsubsection
+
+\stopsection
+
+\startsection[title={\LUATEX\ primitives}]
+
+\startsubsection[title={\tex{ifincsname}}]
+
+As it had no real practical usage uit might get dropped in \LUAMETATEX, so it
+will not be discussed here.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifprimitive}}]
+
+As it had no real practical usage due to limitations, this one is not available
+in \LUAMETATEX\ so it will not be discussed here.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifabsnum}}]
+
+This test is inherited from \PDFTEX\ and behaves like \type {\ifnum} but first
+turns a negative number into a positive one.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifabsdim}}]
+
+This test is inherited from \PDFTEX\ and behaves like \type {\ifdim} but first
+turns a negative dimension into a positive one.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifcondition}}]
+
+This is not really a test but in order to unstand that you need to know how
+\TEX\ internally deals with tests.
+
+\starttyping[option=TEX]
+\ifdimen\scratchdimen>10pt
+ \ifdim\scratchdimen<20pt
+ result a
+ \else
+ result b
+ \fi
+\else
+ result c
+\fi
+\stoptyping
+
+When we end up in the branch of \quotation {result a} we need to skip two \type
+{\else} branches after we're done. The \type {\if..} commands increment a level
+while the \type {\fi} decrements a level. The \type {\else} needs to be skipped
+here. In other cases the true branch needs to be skipped till we end up a the
+right \type {\else}. When doing this skipping, \TEX\ is not interested in what it
+encounters beyond these tokens and this skipping (therefore) goes real fast but
+it does see nested conditions and doesn't interpret grouping related tokens.
+
+A side effect of this is that the next is not working as expected:
+
+\starttyping[option=TEX]
+\def\ifmorethan{\ifdim\scratchdimen>}
+\def\iflessthan{\ifdim\scratchdimen<}
+
+\ifmorethan10pt
+ \iflessthan20pt
+ result a
+ \else
+ result b
+ \fi
+\else
+ result c
+\fi
+\stoptyping
+
+The \type{\iflessthan} macro is not seen as an \type {\if...} so the nesting gets
+messed up. The solution is to fool the scanner in thinking that it is. Say we have:
+
+\startbuffer
+\scratchdimen=25pt
+
+\def\ifmorethan{\ifdim\scratchdimen>}
+\def\iflessthan{\ifdim\scratchdimen<}
+\stopbuffer
+
+\typebuffer[option=TEX] \getbuffer
+
+and:
+
+\startbuffer
+\ifcondition\ifmorethan10pt
+ \ifcondition\iflessthan20pt
+ result a
+ \else
+ result b
+ \fi
+\else
+ result c
+\fi
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+When we expand this snippet we get: \quotation {\inlinebuffer} and no error
+concerning a failure in locating the right \type {\fi's}. So, when scanning the
+\type {\ifcondition} is seen as a valid \type {\if...} but when the condition is
+really expanded it gets ignored and the \type {\ifmorethan} has better come up
+with a match or not.
+
+In this perspective it is also worth mentioning that nesting problems can be
+avoided this way:
+
+\starttyping[option=TEX]
+\def\WhenTrue {something \iftrue ...}
+\def\WhenFalse{something \iffalse ...}
+
+\ifnum\scratchcounter>123
+ \let\next\WhenTrue
+\else
+ \let\next\WhenFalse
+\fi
+\next
+\stoptyping
+
+This trick is mentioned in The \TeX book and can also be found in the plain \TEX\
+format. A variant is this:
+
+\starttyping[option=TEX]
+\ifnum\scratchcounter>123
+ \expandafter\WhenTrue
+\else
+ \expandafter\WhenFalse
+\fi
+\stoptyping
+
+but using \type {\expandafter} can be quite intimidating especially when there
+are multiple in a row. It can also be confusing. Take this: an \type
+{\ifcondition} expects the code that follows to produce a test. So:
+
+\starttyping[option=TEX]
+\def\ifwhatever#1%
+ {\ifdim#1>10pt
+ \expandafter\iftrue
+ \else
+ \expandafter\iffalse
+ \fi}
+
+\ifcondition\ifwhatever{10pt}
+ result a
+\else
+ result b
+\fi
+\stoptyping
+
+This will not work! The reason is in the already mentioned fact that when we end
+up in the greater than \type {10pt} case, the scanner will happily push the \type
+{\iftrue} after the \type {\fi}, which is okay, but when skipping over the \type
+{\else} it sees a nested condition without matching \type {\fi}, which makes ity
+fail. I will spare you a solution with lots of nasty tricks, so here is the clean
+solution using \type {\ifcondition}:
+
+\starttyping[option=TEX]
+\def\truecondition {\iftrue}
+\def\falsecondition{\iffalse}
+
+\def\ifwhatever#1%
+ {\ifdim#1>10pt
+ \expandafter\truecondition
+ \else
+ \expandafter\falsecondition
+ \fi}
+
+\ifcondition\ifwhatever{10pt}
+ result a
+\else
+ result b
+\fi
+\stoptyping
+
+It will be no surprise that the two macros at the top are predefined in \CONTEXT.
+It might be more of a surprise that at the time of this writing the usage in
+\CONTEXT\ of this \type {\ifcondition} primitive is rather minimal. But that
+might change.
+
+As a further teaser I'll show another simple one,
+
+\startbuffer
+\def\HowOdd#1{\unless\ifnum\numexpr ((#1):2)*2\relax=\numexpr#1\relax}
+
+\ifcondition\HowOdd{1}very \else not so \fi odd
+\ifcondition\HowOdd{2}very \else not so \fi odd
+\ifcondition\HowOdd{3}very \else not so \fi odd
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+This renders:
+
+\startlines
+\getbuffer
+\stoplines
+
+The code demonstrates several tricks. First of all we use \type {\numexpr} which
+permits more complex arguments, like:
+
+\starttyping[option=TEX]
+\ifcondition\HowOdd{4+1}very \else not so \fi odd
+\ifcondition\HowOdd{2\scratchcounter+9}very \else not so \fi odd
+\stoptyping
+
+Another trick is that we use an integer division (the \type {:}) which is an
+operator supported by \LUAMETATEX .
+
+\stopsubsection
+
+\stopsection
+
+\startsection[title={\LUAMETATEX\ primitives}]
+
+\startsubsection[title={\tex{ifcmpnum}}]
+
+This one is part of s set of three tests that all are a variant of a \type
+{\ifcase} test. A simple example of the first test is this:
+
+\starttyping[option=TEX]
+\ifcmpnum 123 345 less \or equal \else more \fi
+\stoptyping
+
+The test scans for two numbers, which of course can be registers or expressions,
+and sets the case value to 0, 1 or 2, which means that you then use the normal
+\type {\or} and \type {\else} primitives for follow up on the test.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifchknum}}]
+
+This test scans a number and when it's okay sets the case value to 1, and otherwise
+to 2. So you can do the next:
+
+\starttyping[option=TEX]
+\ifchknum 123\or good \else bad \fi
+\ifchknum bad\or good \else bad \fi
+\stoptyping
+
+An error message is suppressed and the first \type {\or} can be seen as a sort of
+recovery token, although in fact we just use the fast scanner mode that comes
+with the \type {\ifcase}: because the result is 1 or 2, we never see invalid
+tokens.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifnumval}}]
+
+A sort of combination of the previous two is \type {\ifnumval} which checks a
+number but also if it's less, equal or more than zero:
+
+\starttyping[option=TEX]
+\ifnumval 123\or less \or equal \or more \else error \fi
+\ifnumval bad\or less \or equal \or more \else error \fi
+\stoptyping
+
+You can decide to ignore the bad number or do something that makes more sense.
+Often the to be checked value will be the content of a macro or an argument like
+\type {#1}.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifcmpdim}}]
+
+This test is like \type {\ifcmpnum} but for dimensions.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifchkdim}}]
+
+This test is like \type {\ifchknum} but for dimensions.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifdimval}}]
+
+This test is like \type {\ifnumval} but for dimensions.
+
+\stopsubsection
+
+\startsubsection[title={\tex{iftok}}]
+
+Although this test is still experimental it can be used. What happens is that
+two to be compared \quote {things} get scanned for. For each we first gobble
+spaces and \type {\relax} tokens. Then we can have several cases:
+
+\startitemize[n,packed]
+ \startitem
+ When we see a left brace, a list of tokens is scanned upto the
+ matching right brace.
+ \stopitem
+ \startitem
+ When a reference to a token register is seen, that register is taken as
+ value.
+ \stopitem
+ \startitem
+ When a reference to an internal token register is seen, that register is
+ taken as value.
+ \stopitem
+ \startitem
+ When a macro is seen, its definition becomes the to be compared value.
+ \stopitem
+ \startitem
+ When a number is seen, the value of the corresponding register is taken
+ \stopitem
+\stopitemize
+
+An example of the first case is:
+
+\starttyping[option=TEX]
+\iftok {abc} {def}%
+ ...
+\else
+ ...
+\fi
+\stoptyping
+
+The second case goes like this:
+
+\starttyping[option=TEX]
+\iftok\scratchtoksone\scratchtokstwo
+ ...
+\else
+ ...
+\fi
+\stoptyping
+
+Case one and four mixed:
+
+\starttyping[option=TEX]
+\iftok{123}\TempX
+ ...
+\else
+ ...
+\fi
+\stoptyping
+
+The last case is more a catch: it will issue an error when no number is given.
+Eventually that might become a bit more clever (depending on our needs.)
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifcstok}}]
+
+There is a subtle difference between this one and \type {iftok}: spaces
+and \type {\relax} tokens are skipped but nothing gets expanded. So, when
+we arrive at the to be compared \quote {things} we look at what is there,
+as|-|is.
+
+\stopsubsection
+
+\startsubsection[title={\tex{iffrozen}}]
+
+{\em This is an experimental test.} Commands can be defined with the \type
+{\frozen} prefix and this test can be used to check if that has been the case.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifprotected}}]
+
+Commands can be defined with the \type {\protected} prefix (or in \CONTEXT, for
+historic reasons, with \type {\unexpanded}) and this test can be used to check if
+that has been the case.
+
+\stopsubsection
+
+\startsubsection[title={\tex{ifusercmd}}]
+
+{\em This is an experimental test.} It can be used to see if the command is
+defined at the user level or is a build in one. This one might evolve.
+
+\stopsubsection
+
+\startsubsection[title={\tex{orelse}}]
+
+This it not really a test primitive but it does act that way. Say that we have this:
+
+\starttyping[option=TEX]
+\ifdim\scratchdimen>10pt
+ case 1
+\else\ifdim\scratchdimen<20pt
+ case 2
+\else\ifcount\scratchcounter>10
+ case 3
+\else\ifcount\scratchcounter<20
+ case 4
+\fi\fi\fi\fi
+\stoptyping
+
+A bit nicer looks this:
+
+\starttyping[option=TEX]
+\ifdim\scratchdimen>10pt
+ case 1
+\orelse\ifdim\scratchdimen<20pt
+ case 2
+\orelse\ifcount\scratchcounter>10
+ case 3
+\orelse\ifcount\scratchcounter<20
+ case 4
+\fi
+\stoptyping
+
+We stay at the same level and the only test that cannot be used this way is \type
+{\ifcondition} but that is no real problem. Sometimes a more flat test tree had
+advantages but if you think that it gives better performance then you will be
+disappointed. The fact that we stay at the same level is compensated by a bit
+more parsing, so unless you have millions such cases (or expansions) it might
+make a bit of a difference. As mentioned, I'm a bit sensitive for how code looks so
+that was the main motivation for introducing it.
+
+A rather neat trick is the definition of \type {\quitcondition}:
+
+\starttyping[option=TEX]
+\def\quitcondition{\orelse\iffalse}
+\stoptyping
+
+This permits:
+
+\starttyping[option=TEX]
+\ifdim\scratchdimen>10pt
+ case 1a
+ \quitcondition
+ case 4b
+\fi
+\stoptyping
+
+where, of course, the quitting normally is the result of some intermediate extra
+test. But let me play safe here: beware of side effects.
+
+\stopsubsection
+
+\stopsection
+
+\startsection[title={For the brave}]
+
+\startsubsection[title={Full expansion}]
+
+If you don't understand the following code, don't worry. There is seldom much
+reason to go this complex but obscure \TEX\ code attracts some users so \unknown
+
+When you have a macro that has for instance assignments, and when you expand that
+macro inside an \type {\edef}, these assignments are not actually expanded but
+tokenized. In \LUATEX\ there is a way to immediately apply these assignments and
+that feature can be used to write a fully expandable user test. For instance:
+
+\startbuffer
+\def\truecondition {\iftrue}
+\def\falsecondition{\iffalse}
+
+\def\fontwithidhaschar#1#2%
+ {\immediateassignment\scratchcounter\numexpr\fontid\font\relax
+ \immediateassignment\setfontid\numexpr#1\relax
+ \iffontchar\font\numexpr#2\relax
+ \immediateassignment\setfontid\scratchcounter
+ \expandafter\truecondition
+ \else
+ \expandafter\falsecondition
+ \fi}
+\stopbuffer
+
+\typebuffer[option=TEX] \getbuffer
+
+The \type {\iffontchar} test doesn't handle numeric font id, simply because
+at the time it was added to \ETEX, there was no access to these id's. Now we
+can do:
+
+\startbuffer
+\edef\foo{\fontwithidhaschar{1} {75}yes\else nop\fi} \meaning\foo
+\edef\foo{\fontwithidhaschar{1}{999}yes\else nop\fi} \meaning\foo
+
+[\ifcondition\fontwithidhaschar{1} {75}yes\else nop\fi]
+[\ifcondition\fontwithidhaschar{1}{999}yes\else nop\fi]
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+These result in:
+
+\startlines
+\getbuffer
+\stoplines
+
+If you remove the \type {\immediateassignment} in the definition above then the
+typeset results are still the same but the meanings of \type {\foo} look
+different: they contain the assignments and the test for the character is
+actually done when constructing the content of the \type {\edef}, but for the
+current font. So, basically that test is now useless.
+
+\stopsubsection
+
+\startsubsection[title={User defined if's}]
+
+There is a \type {\newif} macro that defines three other macros:
+
+\starttyping[option=TEX]
+\newif\ifOnMyOwnTerms
+\stoptyping
+
+After this, not only \type {\ifOnMyOwnTerms} is defined, but also:
+
+\starttyping[option=TEX]
+\OnMyOwnTermstrue
+\OnMyOwnTermsfalse
+\stoptyping
+
+These two actually are macros that redefine \type {\ifOnMyOwnTerms} to be either
+equivalent to \type {\iftrue} and \type {\iffalse}. The (often derived from plain
+\TEX) definition of \type {\newif} is a bit if a challenge as it has to deal with
+removing the \type {if} in order to create the two extra macros and also make
+sure that it doesn't get mixed up in a catcode jungle.
+
+In \CONTEXT\ we have a variant:
+
+\starttyping[option=TEX]
+\newconditional\MyConditional
+\stoptyping
+
+that can be used with:
+
+\starttyping[option=TEX]
+\settrue\MyConditional
+\setfalse\MyConditional
+\stoptyping
+
+and tested like:
+
+\starttyping[option=TEX]
+\ifconditional\MyConditional
+ ...
+\else
+ ...
+\fi
+\stoptyping
+
+This one is cheaper on the hash and doesn't need the two extra macros per test.
+The price is the use of \type {\ifconditional}, which is {\em not} to confused
+with \type {\ifcondition} (it has bitten me already a few times).
+
+\stopsubsection
+
+\stopsection
+
+\startsubject[title=Colofon]
+
+\starttabulate
+\NC Author \NC Hans Hagen \NC \NR
+\NC \CONTEXT \NC \contextversion \NC \NR
+\NC \LUAMETATEX \NC \texengineversion \NC \NR
+\NC Support \NC www.pragma-ade.com \NC \NR
+\NC \NC contextgarden.net \NC \NR
+\stoptabulate
+
+\stopsubject
+
+\stopdocument
diff --git a/doc/context/sources/general/manuals/lowlevel/lowlevel-expansion.tex b/doc/context/sources/general/manuals/lowlevel/lowlevel-expansion.tex
new file mode 100644
index 000000000..1e2e00a35
--- /dev/null
+++ b/doc/context/sources/general/manuals/lowlevel/lowlevel-expansion.tex
@@ -0,0 +1,442 @@
+% language=us
+
+\environment lowlevel-style
+
+\startdocument
+ [title=expansion,
+ color=middleyellow]
+
+\startsection[title=Preamble]
+
+% \startsubsection[title=Introduction]
+% \stopsubsection
+
+This short manual demonstrates a couple of properties of the macro language. It
+is not the in|-|depth philosophical expose about macro languages, tokens,
+expansion and such that some \TEX ies like. I prefer to stick to the practical
+aspects.
+
+\stopsection
+
+\startsection[title={\TEX\ primitives}]
+
+The \TEX\ language provides quite some commands and those built in are called
+primitives. User defined commands are called macros. A macro is a shortcut to a
+list of primitives or macro calls. All can be mixed with characters that are to
+be typeset somehow.
+
+\starttyping[option=TEX]
+\def\MyMacro{b}
+
+a\MyMacro c
+\stoptyping
+
+When \TEX\ reads this input the \type {a} gets turned into a glyph node with a
+reference to the current font set and the character \type {a}. Then the parser
+sees a macro call, and it will enter another input level where it expands this
+macro. In this case it sees just an \type {b} and it will give this the same
+treatment as the \type {a}. The macro ends, the input level decrements and the
+\type {c} gets its treatment.
+
+A macro can contain references to macros so in practice the input can go several
+levels down.
+
+\starttyping[option=TEX]
+\def\MyMacroA{ and }
+\def\MyMacroB{1\MyMacroA 2}
+
+a\MyMacroA b
+\stoptyping
+
+When \type {\MyMacroB} is defined, its body gets three so called tokens: the
+character token \type {a} with property \quote {other}, a token that is a
+reference to the macro \type {\MyMacroB}, and a character token \type {2}, also
+with property \quote {other} The meaning of \type {\MyMacroA} became five tokens:
+a reference to a space token, then three character tokens with property \quote
+{letter}, and finally again a space token.
+
+\starttyping[option=TEX]
+\def \MyMacroA{ and }
+\edef\MyMacroB{1\MyMacroA 2}
+
+a\MyMacroA b
+\stoptyping
+
+In the previous example an \type {\edef} is used, where the \type {e} indicates
+expansion. This time the meaning gets expanded. So we get effectively the same
+as
+
+\starttyping[option=TEX]
+\def\MyMacroB{1 and 2}
+\stoptyping
+
+Characters are easy: they just expand, but not all primitives expand to their
+meaning or effect.
+
+\startbuffer
+\def\MyMacroA{\scratchcounter = 1 }
+\def\MyMacroB{\advance\scratchcounter by 1}
+\def\MyMacroC{\the\scratchcounter}
+
+\MyMacroA a
+\MyMacroB b
+\MyMacroB c
+\MyMacroB d
+\MyMacroC
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\scratchcounter0 \getbuffer
+
+\startlines \tt
+\meaning\MyMacroA
+\meaning\MyMacroB
+\meaning\MyMacroC
+\stoplines
+
+Let's assume that \type {\scratchcounter} is zero to start with and use \type
+{\edef's}:
+
+\startbuffer
+\edef\MyMacroA{\scratchcounter = 1 }
+\edef\MyMacroB{\advance\scratchcounter by 1}
+\edef\MyMacroC{\the\scratchcounter}
+
+\MyMacroA a
+\MyMacroB b
+\MyMacroB c
+\MyMacroB d
+\MyMacroC
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\scratchcounter0 \getbuffer
+
+\startlines \tt
+\meaning\MyMacroA
+\meaning\MyMacroB
+\meaning\MyMacroC
+\stoplines
+
+So, this time the third macro has basically its meaning frozen, but we can
+prevent this by applying a \type {\noexpand} when we do this:
+
+\startbuffer
+\edef\MyMacroA{\scratchcounter = 1 }
+\edef\MyMacroB{\advance\scratchcounter by 1}
+\edef\MyMacroC{\noexpand\the\scratchcounter}
+
+\MyMacroA a
+\MyMacroB b
+\MyMacroB c
+\MyMacroB d
+\MyMacroC
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\scratchcounter0 \getbuffer
+
+\startlines \tt
+\meaning\MyMacroA
+\meaning\MyMacroB
+\meaning\MyMacroC
+\stoplines
+
+Of course this is a rather useless example but it serves its purpose: you'd better
+be aware what gets expanded immediately in an \type {\edef}. In most cases you
+only need to worry about \type {\the} and embedded macros (and then of course
+their meanings).
+
+\def\MyShow{\quotation {\strut \inlinebuffer \expandafter \typ \expandafter
+{\the\scratchtoks}\strut}}
+
+You can also store tokens in a so called token register. Here we use a predefined
+scratch register:
+
+\startbuffer
+\def\MyMacroA{ and }
+\def\MyMacroB{1\MyMacroA 2}
+\scratchtoks {\MyMacroA}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+The content of \type {\scratchtoks} is: \MyShow, so no expansion has happened
+here.
+
+\startbuffer
+\def\MyMacroA{ and }
+\def\MyMacroB{1\MyMacroA 2}
+\scratchtoks \expandafter {\MyMacroA}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+Now the content of \type {\scratchtoks} is: \MyShow, so this time expansion has
+happened.
+
+\startbuffer
+\def\MyMacroA{ and }
+\def\MyMacroB{1\MyMacroA 2}
+\scratchtoks \expandafter {\MyMacroB}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+Indeed the macro gets expanded but only one level: \MyShow. Compare this with:
+
+\startbuffer
+\def\MyMacroA{ and }
+\edef\MyMacroB{1\MyMacroA 2}
+\scratchtoks \expandafter {\MyMacroB}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+The trick is to expand in two steps: \MyShow. Later we will see that other
+engines provide some more expansion tricks. The only way to get a grip on
+expansion is to just play with it.
+
+The \type {\expandafter} primitive expands the token (which can be a macro) after
+the next next one and injects its meaning into the stream. So:
+
+\starttyping[option=TEX]
+\expandafter \MyMacroA \MyMacroB
+\stoptyping
+
+works okay. In a normal document you will never need this kind of hackery: it
+only happens in a bit more complex macros. Here is an example:
+
+\startbuffer[a]
+\scratchcounter 1
+\bgroup
+\advance\scratchcounter 1
+\egroup
+\the\scratchcounter
+\stopbuffer
+
+\typebuffer[a][option=TEX]
+
+\startbuffer[b]
+\scratchcounter 1
+\bgroup
+\advance\scratchcounter 1
+\expandafter
+\egroup
+\the\scratchcounter
+\stopbuffer
+
+\typebuffer[b][option=TEX]
+
+The first one gives \inlinebuffer[a], while the second gives \inlinebuffer[b].
+
+% \let
+% \futurelet
+% \afterassignment
+% \aftergroup
+
+\stopsection
+
+\startsection[title={\ETEX\ primitives}]
+
+In this engine a couple of extensions were added and later on \PDFTEX\ added some
+more. We only discuss a few that relate to expansion. There is however a pitfall
+here. Before \ETEX\ showed up, \CONTEXT\ already had a few mechanism that also
+related to expansion and it used some names for macros that clash with those in
+\ETEX. This is why we will use the \type {\normal} prefix here to indicate the
+primitive.
+
+\startbuffer
+\def\MyMacroA{a}
+\def\MyMacroB{b}
+\normalprotected\def\MyMacroC{c}
+\edef\MyMacroABC{\MyMacroA\MyMacroB\MyMacroC}
+\stopbuffer
+
+\typebuffer[option=TEX] \getbuffer
+
+These macros have the following meanings:
+
+\startlines \tt
+\meaning\MyMacroA
+\meaning\MyMacroB
+\meaning\MyMacroC
+\meaning\MyMacroABC
+\stoplines
+
+In \CONTEXT\ you will use the \type {\unexpanded} prefix instead because that one
+did something similar in older versions of \CONTEXT. As we were early adopters of
+\ETEX, this later became a synonym to the \ETEX\ primitive.
+
+\startbuffer
+\def\MyMacroA{a}
+\def\MyMacroB{b}
+\normalprotected\def\MyMacroC{c}
+\normalexpanded{\scratchtoks{\MyMacroA\MyMacroB\MyMacroC}}
+\stopbuffer
+
+\typebuffer[option=TEX] \getbuffer
+
+Here the wrapper around the token register assignment will expand the three
+macros, unless they are protected, so its content becomes \MyShow. This saves
+either a lot of more complex \type {\expandafter} usage or using an intermediate
+\type {\edef}. In \CONTEXT\ the \type {\expanded} macro does something simpler
+but it doesn't expand the first token as it is meant as a wrapper around a command,
+like:
+
+\starttyping[option=TEX]
+\expanded{\chapter{....}} % a ConTeXt command
+\stoptyping
+
+where we do want to expand the title but not the \type {\chapter} command, not
+that this would happen actually because \type {\chapter} is a protected command.
+
+The counterpart of \type {\normalexpanded} is \type {\normalunexpanded}, as in:
+
+\startbuffer
+\def\MyMacroA{a}
+\def\MyMacroB{b}
+\normalprotected\def\MyMacroC{c}
+\normalexpanded {\scratchtoks
+ {\MyMacroA\normalunexpanded {\MyMacroB}\MyMacroC}}
+\stopbuffer
+
+\typebuffer[option=TEX] \getbuffer
+
+The register now holds \MyShow: three tokens, one character token and two
+macro references.
+
+Tokens can represent characters, primitives, macros or be special entities like
+starting math mode, beginning a group, assigning a dimension to a register, etc.
+Although you can never really get back to the original input, you can come pretty
+close, with:
+
+\startbuffer
+\normaldetokenize{this can $ be anything \bgroup}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+This (when typeset monospaced) is: {\tt \inlinebuffer}. The detokenizer is like
+\type {\string} applied to each token in its argument. Compare this:
+
+\startbuffer
+\normalexpanded {
+ \normaldetokenize{10pt}
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+We get four tokens: {\tt\inlinebuffer}.
+
+\startbuffer
+\normalexpanded {
+ \string 1\string 0\string p\string t
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+So that was the same operation: {\tt\inlinebuffer}, but in both cases there is a
+subtle thing going on: characters have a catcode which distinguishes them. The
+parser needs to know what makes up a command name and normally that's only
+letters. The next snippet shows these catcodes:
+
+\startbuffer
+\normalexpanded {
+ \noexpand\the\catcode`\string 1 \noexpand\enspace
+ \noexpand\the\catcode`\string 0 \noexpand\enspace
+ \noexpand\the\catcode`\string p \noexpand\enspace
+ \noexpand\the\catcode`\string t \noexpand
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+The result is \quotation {\tt\inlinebuffer}: two characters are marked as \quote
+{letter} and two fall in the \quote {other} category.
+
+\stopsection
+
+\startsection[title={\LUATEX\ primitives}]
+
+This engine adds a little in the expansion arena. First of all it offers a way to
+extend token lists registers
+
+\startbuffer
+\def\MyMacroA{a}
+\def\MyMacroB{b}
+\normalprotected\def\MyMacroC{b}
+\scratchtoks{\MyMacroA\MyMacroB}
+\stopbuffer
+
+\typebuffer[option=TEX] \getbuffer
+
+The result is: \MyShow.
+
+\startbuffer
+\toksapp\scratchtoks{\MyMacroA\MyMacroB}
+\stopbuffer
+
+\typebuffer[option=TEX] \getbuffer
+
+We're now at: \MyShow.
+
+\startbuffer
+\etoksapp\scratchtoks{\MyMacroA\space\MyMacroB\space\MyMacroC}
+\stopbuffer
+
+\typebuffer[option=TEX] \getbuffer
+
+The register has this content: \MyShow, so the additional context got expanded in
+the process, except of course the protected macro \type {\MyMacroC}.
+
+There is a bunch of these combiners: \type {\toksapp} and \type {\tokspre} for
+local appending and prepending, with global companions: \type {\gtoksapp} and
+\type {\gtokspre}, as well as expanding variant: \type {\etoksapp}, \type
+{\etokspre}, \type {\xtoksapp} and \type {\xtokspre}.
+
+There are not beforehand more efficient that using intermediate expanded macros
+or token lists, simply because in the process \TEX\ has to create tokens lists
+too, but sometimes they're just more convenient to use.
+
+A second extension is \type {\immediateassignment} which instead in tokenizing
+the assignment directive applies it right now.
+
+\startbuffer
+\edef\MyMacroA
+ {\scratchcounter 123
+ \noexpand\the\scratchcounter}
+
+\edef\MyMacroB
+ {\immediateassignment\scratchcounter 123
+ \noexpand\the\scratchcounter}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\getbuffer
+
+These two macros now have the meaning:
+
+\startlines \tt
+\meaning\MyMacroA
+\meaning\MyMacroB
+\stoplines
+
+\stopsection
+
+\startsection[title={\LUAMETATEX\ primitives}]
+
+{\em todo}
+
+% \aftergroups
+
+\stopsection
+
+\stopdocument
+
diff --git a/doc/context/sources/general/manuals/lowlevel/lowlevel-registers.tex b/doc/context/sources/general/manuals/lowlevel/lowlevel-registers.tex
new file mode 100644
index 000000000..8ccb0cd3a
--- /dev/null
+++ b/doc/context/sources/general/manuals/lowlevel/lowlevel-registers.tex
@@ -0,0 +1,251 @@
+% language=us
+
+\environment lowlevel-style
+
+\startdocument
+ [title=registers,
+ color=darkmagenta]
+
+\startsection[title=Preamble]
+
+Registers are sets of variables that are accessed by index and a such resemble
+registers in a processing unit. You can store a quantity in a register, retrieve
+it, and also manipulate it.
+
+There is hardly any need to use them in \CONTEXT\ so we keep it simple.
+
+\stopsection
+
+\startsection[title={\TEX\ primitives}]
+
+There are several categories:
+
+\startitemize
+\startitem
+ Integers (int): in order to be portable (at the time it surfaced) there are only
+ integers and no floats. The only place where \TEX\ uses floats internally is
+ when glue gets effective which happens in the backend.
+\stopitem
+\startitem
+ Dimensions (dimen): internally these are just integers but when they are entered they
+ are sliced into two parts so that we have a fractional part. The internal
+ representation is called a scaled point.
+\stopitem
+\startitem
+ Glue (skip): these are dimensions with a few additional properties: stretch and
+ shrink. Being a compound entity they are stored differently and thereby a bit
+ less efficient than numbers and dimensions.
+\stopitem
+\startitem
+ Math glue (muskip): this is the same as glue but with a unit that adapts to
+ the current math style properties. It's best to think about them as being
+ relative measures.
+\stopitem
+\startitem
+ Token lists (toks): these contain a list of tokens coming from the input
+ or coming from a place where they already have been converted.
+\stopitem
+\stopitemize
+
+The original \TEX\ engine had 256 entries per set. The first ten of each set are
+normally reserved for scratch purposes: the even ones for local use, and the odd
+ones for global usage. On top of that macro packages can reserve some for its own
+use. It was quite easy to reach the maximum but there were tricks around that.
+This limitation is no longer present in the variants in use today.
+
+Let's set a few dimension registers:
+
+\startbuffer[1]
+\dimen 0 = 10 pt
+\dimen2=10pt
+\dimen4 10pt
+\scratchdimen 10pt
+\stopbuffer
+
+\typebuffer[1][option=TEX]
+
+We can serialize them with:
+
+\startbuffer[2]
+\the \dimen0
+\number \dimen2
+\meaning\dimen4
+\meaning\scratchdimen
+\stopbuffer
+
+\typebuffer[2][option=TEX]
+
+The results of these operations are:
+
+\startlines\tt
+\getbuffer[1,2]
+\stoplines
+
+The last two is not really useful but it is what you see when tracing options are
+set. Here \type {\scratchdimen} is a shortcut for a register. This is {\em not} a
+macro but a defined register. The low level \type {\dimendef} is used for this
+but in a macro package you should not use that one but the higher level \type
+{\newdimen} macro that uses it.
+
+\startbuffer[1]
+\newdimen\MyDimenA
+\def \MyDimenB{\dimen999}
+\dimendef\MyDimenC998
+\stopbuffer
+
+\typebuffer[1][option=TEX]
+
+\startbuffer[2]
+\meaning\MyDimenA
+\meaning\MyDimenB
+\meaning\MyDimenC
+\stopbuffer
+
+\typebuffer[2][option=TEX]
+
+Watch the difference:
+
+\startlines\tt
+\getbuffer[1,2]
+\stoplines
+
+The first definition uses a yet free register so you won't get a clash. The
+second one is just a shortcut using a macro and the third one too but again
+direct shortcut. Try to imagine how the second line gets interpreted:
+
+\starttyping[option=TEX]
+\MyDimenA10pt \MyDimenA10.5pt
+\MyDimenB10pt \MyDimenB10.5pt
+\MyDimenC10pt \MyDimenC10.5pt
+\stoptyping
+
+Also try to imagine what messing around with \type {\MyDimenC} will do when we
+also have defined a few hundred extra dimensions with \type {\newdimen}.
+
+In the case of dimensions the \type {\number} primitive will make the register
+serialize as scaled points without unit \type {sp}.
+
+Next we see some of the other registers being assigned:
+
+\starttyping[option=TEX]
+\count 0 = 100
+\skip 0 = 10pt plus 3pt minus 2pt
+\skip 0 = 10pt plus 1fill
+\muskip 0 = 10mu plus 3mu minus 2mu
+\muskip 0 = 10mu minus 1 fil
+\toks 0 = {hundred}
+\stoptyping
+
+When a number is expected, you can use for instance this:
+
+\starttyping[option=TEX]
+\scratchcounter\scratchcounterone
+\stoptyping
+
+Here we use a few predefined scratch registers. You can also do this:
+
+\starttyping[option=TEX]
+\scratchcounter\numexpr\scratchcounterone+\scratchcountertwo\relax
+\stoptyping
+
+There are some quantities that also qualify as number:
+
+\starttyping[option=TEX]
+\chardef\MyChar=123 % refers to character 123 (if present)
+\scratchcounter\MyChar
+\stoptyping
+
+In the past using \type {\chardef} was a way to get around the limited number of
+registers, but it still had (in traditional \TEX) a limitation: you could not go
+beyond 255. The \type {\mathchardef} could fo higher as it also encodes a family
+number and class. This limitation has been lifted in \LUATEX.
+
+A character itself can also be interpreted as number, in which case it has to be
+prefixed with a reverse quote: \type {`}, so:
+
+\startbuffer
+\scratchcounter\numexpr`0+5\relax
+\char\scratchcounter
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+produces \quotation {\inlinebuffer} because the \type {`0} expands into the
+(\ASCII\ and \UTF8) slot {\tt \number`0} which represents the character zero. In
+this case the next makes more sense:
+
+\starttyping[option=TEX]
+\char\numexpr`0+5\relax
+\stoptyping
+
+If you want to know more about all these quantities, \quotation {\TEX\ By Topic}
+provides a good summary of what \TEX\ has to offer, and there is no need to repeat
+it here.
+
+\stopsection
+
+\startsection[title={\ETEX\ primitives}]
+
+Apart from the ability to use expressions, the contribution to registers that
+\ETEX\ brought was that suddenly we could use upto 65K of them, which is more
+than enough. The extra registers were not as efficient as the first 256 because
+they were stored in the hash table, but that was not really a problem. In \OMEGA\
+and later \LUATEX\ regular arrays were used, at the cost of more memory which in
+the meantime has become cheap. As \CONTEXT\ moved to \ETEX\ rather early its
+users never had to worry about it.
+
+\stopsection
+
+\startsection[title={\LUATEX\ primitives}]
+
+The \LUATEX\ engine introduced attributes. These are numeric properties that are
+bound to the nodes that are the result of typesetting operations. They are
+basically like integer registers but when set their values get bound and when
+unset they are kind of invisible.
+
+\startitemize
+\startitem
+ Attribute (attribute): a numeric property that when set becomes part of the
+ current attribute list that gets assigned to nodes.
+\stopitem
+\stopitemize
+
+Attributes can be used to communicate properties to \LUA\ callbacks. There are
+several functions available for setting them and querying them.
+
+\starttyping[option=TEX]
+\attribute999 = 123
+\stoptyping
+
+Using attributes this way is dangerous (of course I can only speak for \CONTEXT)
+because an attribute value might trigger some action in a callback that gives
+unwanted side effects. For convenience \CONTEXT\ provides:
+
+\startbuffer
+\newattribute\MyAttribute
+\stopbuffer
+
+\typebuffer[option=TEX] \getbuffer
+
+Which currently defines \type {\MyAttribute} as {\tt \meaning\MyAttribute} and is
+meant to be used as: \footnote {The low level \type {\attributedef} command is
+rather useless in the perspective of \CONTEXT.}
+
+\starttyping[option=TEX]
+\attribute\MyAttribute = 123
+\stoptyping
+
+Just be aware that defining attributes can have an impact on performance. As you
+cannot access them at the \TEX\ end you seldom need them. If you do you can
+better use the proper more high level definers (not discussed here).
+
+\stopsection
+
+\startsection[title={\LUAMETATEX\ primitives}]
+
+{\em todo}
+
+\stopsection
+
+\stopdocument
+
diff --git a/doc/context/sources/general/manuals/lowlevel/lowlevel-style.tex b/doc/context/sources/general/manuals/lowlevel/lowlevel-style.tex
new file mode 100644
index 000000000..ddd9df747
--- /dev/null
+++ b/doc/context/sources/general/manuals/lowlevel/lowlevel-style.tex
@@ -0,0 +1,104 @@
+% language=us
+
+% I started this series in June 2019 and I bet that it will never be complete or
+% extensive enough. But I'll do my best to make it as useful as possible ConTeXt
+% users out there who like to know about such details. Feel free to ask for more
+% explanations.
+
+\startenvironment lowlevel-style
+
+\usemodule[abbreviations-logos]
+\usemodule[scite]
+
+\setvariables
+ [document]
+ [title=No Title,
+ author=No Author,
+ color=NoColor]
+
+\setupbodyfont
+ [dejavu,11pt]
+
+\setuplayout
+ [width=middle,
+ height=middle,
+ backspace=2cm,
+ topspace=15mm]
+
+\setupwhitespace
+ [big]
+
+\setuphead
+ [chapter]
+ [style=\bfc,
+ color=darkgray]
+
+\setuphead
+ [section]
+ [style=\bfb,
+ %page=right,
+ color=darkgray]
+
+\setuphead
+ [subsection]
+ [style=\bfa,
+ color=darkgray]
+
+\setupfootertexts
+ [section] % [\documentvariable{title}]
+
+\setupfooter
+ [style=bold,
+ color=darkgray]
+
+\startuseMPgraphic{titlepage}
+ fill Page
+ withcolor "\documentvariable{color}" ;
+
+ numeric d ; d := 2mm ;
+
+ picture p ; p := textext.llft("\tex{}")
+ xysized (.1PaperWidth-2d,.1PaperHeight-2d)
+ shifted (.1PaperWidth- d,.1PaperHeight -d)
+ ;
+
+ draw image (
+ for i = 0 step .1 PaperWidth until PaperWidth :
+ for j = 0 step .1 PaperHeight until PaperHeight :
+ draw p shifted (i,j) ;
+ endfor ;
+ endfor ;
+ ) withcolor .5resolvedcolor("middlegray") ;
+
+ draw textext.d("\strut low level")
+ xsized (.8PaperWidth)
+ shifted center topboundary Page
+ shifted -(0,.2PaperHeight)
+ withcolor "white" ;
+ draw textext.d("\strut \TeX")
+ xsized (.4PaperWidth)
+ shifted center topboundary Page
+ shifted -(0,.4PaperHeight)
+ withcolor "white" ;
+ draw textext.d("\strut\documentvariable{title}")
+ ysized 3cm
+ shifted center bottomboundary Page
+ shifted (0,.1PaperHeight)
+ withcolor "white" ;
+\stopuseMPgraphic
+
+\startsetups document:start
+
+ \startMPpage
+ \includeMPgraphic{titlepage} ;
+ \stopMPpage
+
+ \page
+
+ \startsubject[title=Contents]
+ \placelist[section][criterium=previous]
+ \stopsubject
+
+\stopsetups
+
+\stopenvironment
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-arrow.tex b/doc/context/sources/general/manuals/luametafun/luametafun-arrow.tex
new file mode 100644
index 000000000..72c9528e8
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-arrow.tex
@@ -0,0 +1,166 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-arrow
+
+\startchapter[title={Arrow}]
+
+Arrows are somewhat complicated because they follow the path, are constructed
+using a pen, have a fill and draw, and need to scale. One problem is that the
+size depends on the pen but the pen normally is only known afterwards.
+
+\startbuffer[1a]
+\startMPcode
+draw lmt_arrow [
+ path = (fullcircle scaled 3cm),
+]
+ withpen pencircle scaled 2mm
+ withcolor "darkred" ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[1b]
+\startMPcode
+draw lmt_arrow [
+ path = (fullcircle scaled 3cm),
+ length = 8,
+]
+ withpen pencircle scaled 2mm
+ withcolor "darkgreen" ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[1c]
+\startMPcode
+draw lmt_arrow [
+ path = (fullcircle scaled 3cm rotated 45),
+ pen = (pencircle xscaled 2mm yscaled 1mm rotated 45),
+]
+ withpen pencircle xscaled 2mm yscaled 1mm rotated 45
+ withcolor "darkblue" ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[1d]
+\startMPcode
+pickup pencircle xscaled 2mm yscaled 1mm rotated 45 ;
+draw lmt_arrow [
+ path = (fullcircle scaled 3cm rotated 45),
+ pen = "auto",
+]
+ withcolor "darkyellow" ;
+\stopMPcode
+\stopbuffer
+
+To some extent \METAFUN\ can help you with this issue. In \in {figure} [arrows:1]
+we see some variants. The definitions are given below:
+
+\typebuffer[1a][option=TEX]
+\typebuffer[1b][option=TEX]
+\typebuffer[1c][option=TEX]
+\typebuffer[1d][option=TEX]
+
+\startplacefigure[reference=arrows:1]
+ \startcombination[4*1]
+ {\getbuffer[1a]} {default}
+ {\getbuffer[1b]} {length}
+ {\getbuffer[1c]} {pen}
+ {\getbuffer[1d]} {auto}
+ \stopcombination
+\stopplacefigure
+
+There are some options that influence the shape of the arrowhead and its
+location on the path. You can for instance ask for two arrowheads:
+
+\startbuffer[3]
+\startMPcode
+ pickup pencircle scaled 1mm ;
+ draw lmt_arrow [
+ pen = "auto",
+ location = "both"
+ path = fullcircle scaled 3cm rotated 90,
+ ] withcolor "darkgreen" ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[3][option=TEX]
+
+\startlinecorrection
+\getbuffer[3]
+\stoplinecorrection
+
+The shape can also be influenced although often this is not that visible:
+
+\startbuffer[4]
+\startMPcode
+ pickup pencircle scaled 1mm ;
+ draw lmt_arrow [
+ kind = "draw",
+ pen = "auto",
+ penscale = 4,
+ location = "middle",
+ alternative = "curved",
+ path = fullcircle scaled 3cm,
+ ] withcolor "darkblue" ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[4][option=TEX]
+
+\startlinecorrection
+\getbuffer[4]
+\stoplinecorrection
+
+The location can also be given as percentage, as this example demonstrates. Watch
+how we draw only arrow heads:
+
+\startbuffer[5]
+\startMPcode
+ pickup pencircle scaled 1mm ;
+ for i = 0 step 5 until 100 :
+ draw lmt_arrow [
+ alternative = "dimpled",
+ pen = "auto",
+ location = "percentage",
+ percentage = i,
+ dimple = (1/5 + i/200),
+ headonly = (i = 0),
+ path = fullcircle scaled 3cm,
+ ] withcolor "darkyellow" ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[5][option=TEX]
+
+\startlinecorrection
+\getbuffer[5]
+\stoplinecorrection
+
+The supported parameters are:
+
+\starttabulate[|T|T|T|p|]
+\FL
+\BC name \BC type \BC default \BC comment \NC \NR
+\ML
+\NC path \NC path \NC \NC \NC \NR
+\NC pen \NC path \NC \NC \NC \NR
+\NC \NC string \NC auto \NC \NC \NR
+\NC kind \NC string \NC fill \NC \type {fill} or \type {draw} \NC \NR
+\NC dimple \NC numeric \NC 1/5 \NC \NC \NR
+\NC scale \NC numeric \NC 3/4 \NC \NC \NR
+\NC penscale \NC numeric \NC 3 \NC \NC \NR
+\NC length \NC numeric \NC 4 \NC \NC \NR
+\NC angle \NC numeric \NC 45 \NC \NC \NR
+\NC location \NC string \NC end \NC \type {end}, \type {middle} or \type {both} \NC \NR % middle both
+\NC alternative \NC string \NC normal \NC \type {normal}, \type {dimpled} or \type {curved} \NC \NR
+\NC percentage \NC numeric \NC 50 \NC \NC \NR
+\NC headonly \NC boolean \NC false \NC \NC \NR
+\LL
+\stoptabulate
+
+\stopchapter
+
+\stopcomponent
+
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-axis.tex b/doc/context/sources/general/manuals/luametafun/luametafun-axis.tex
new file mode 100644
index 000000000..f9483039c
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-axis.tex
@@ -0,0 +1,83 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-axis
+
+\startchapter[title={Axis}]
+
+The axis macro is the result of one of the first experiments with the key|/|value
+interface in \METAFUN. Let's show a lot in one example:
+
+\startbuffer[1]
+\startMPcode
+ draw lmt_axis [
+ sx = 5mm, sy = 5mm,
+ nx = 20, ny = 10,
+ dx = 5, dy = 2,
+ tx = 10, ty = 10,
+
+ list = {
+ [
+ connect = true,
+ color = "darkred",
+ close = true,
+ points = { (1, 1), (15, 8), (2, 10) },
+ texts = { "segment 1", "segment 2", "segment 3" }
+ ],
+ [
+ connect = true,
+ color = "darkgreen",
+ points = { (2, 2), (4, 1), (10, 3), (16, 8), (19, 2) },
+ labels = { "a", "b", "c", "d", "e" }
+ ],
+ [
+ connect = true,
+ color = "darkblue",
+ close = true,
+ points = { (5, 3), (8, 8), (16, 1) },
+ labels = { "1", "2", "3" }
+ ]
+ },
+
+ ] withpen pencircle scaled 1mm ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[1][option=TEX]
+
+\startplacefigure[reference=axis:1]
+ \getbuffer[1]
+\stopplacefigure
+
+This macro will probably be extended at some point.
+
+\starttabulate[|T|T|T|p|]
+\FL
+\BC name \BC type \BC default \BC comment \NC \NR
+\ML
+\NC nx \NC numeric \NC 1 \NC \NC \NR
+\NC dx \NC numeric \NC 1 \NC \NC \NR
+\NC tx \NC numeric \NC 0 \NC \NC \NR
+\NC sx \NC numeric \NC 1 \NC \NC \NR
+\NC startx \NC numeric \NC 0 \NC \NC \NR
+\NC ny \NC numeric \NC 1 \NC \NC \NR
+\NC dy \NC numeric \NC 1 \NC \NC \NR
+\NC ty \NC numeric \NC 0 \NC \NC \NR
+\NC sy \NC numeric \NC 1 \NC \NC \NR
+\NC starty \NC numeric \NC 0 \NC \NC \NR
+\ML \NC \NC \NR
+\NC samples \NC list \NC \NC \NC \NR
+\NC list \NC list \NC \NC \NC \NR
+\NC connect \NC boolean \NC false \NC \NC \NR
+\NC list \NC list \NC \NC \NC \NR
+\NC close \NC boolean \NC false \NC \NC \NR
+\NC samplecolors \NC list \NC \NC \NC \NR
+\NC axiscolor \NC string \NC \NC \NC \NR
+\NC textcolor \NC string \NC \NC \NC \NR
+\LL
+\stoptabulate
+
+\stopchapter
+
+\stopcomponent
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-chart.tex b/doc/context/sources/general/manuals/luametafun/luametafun-chart.tex
new file mode 100644
index 000000000..1bd89d350
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-chart.tex
@@ -0,0 +1,441 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-chart
+
+\startchapter[title={Chart}]
+
+This is another example implementation but it might be handy for simple cases of
+presenting results. Of course one can debate the usefulness of certain ways of
+presenting but here we avoid that discussion. Let's start with a simple pie
+chart (\in {figure} [chart:1]).
+
+\startbuffer[1]
+\startMPcode
+ draw lmt_chart_circle [
+ samples = { { 1, 4, 3, 2, 5, 7, 6 } },
+ percentage = true,
+ trace = true,
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[1][option=TEX]
+
+\startplacefigure[reference=chart:1]
+ \getbuffer[1]
+\stopplacefigure
+
+As with all these \LMTX\ extensions, you're invited to play with the parameters.
+in \in {figure} [chart:2] we see a variant that adds labels as well as one that
+has a legend.
+
+\startbuffer[2a]
+\startMPcode
+draw lmt_chart_circle [
+ height = 4cm,
+ samples = { { 1, 4, 3, 2, 5, 7, 6 } },
+ percentage = true,
+ trace = true,
+ labelcolor = "white",
+ labelformat = "@0.1f",
+ labelstyle = "ttxx"
+] ;
+\stopMPcode
+\stopbuffer
+
+The styling of labels and legends can be influenced independently.
+
+\typebuffer[2a][option=TEX]
+
+\startbuffer[2b]
+\startMPcode
+draw lmt_chart_circle [
+ height = 4cm,
+ samples = { { 1, 4, 3, 2, 5, 7, 6 } },
+ percentage = false,
+ trace = true,
+ linewidth = .125mm,
+ originsize = 0,
+ labeloffset = 3cm,
+ labelstyle = "bfxx",
+ legendstyle = "tfxx",
+ legend = {
+ "first", "second", "third", "fourth",
+ "fifth", "sixths", "sevenths"
+ }
+] ;
+
+\stopMPcode
+\stopbuffer
+
+\typebuffer[2b][option=TEX]
+
+\startplacefigure[reference=chart:2]
+ \startcombination
+ {\getbuffer[2a]} {}
+ {\getbuffer[2b]} {}
+ \stopcombination
+\stopplacefigure
+
+A second way of rendering are histograms, and the interface is mostly the same.
+In \in {figure} [chart:3] we see two variants
+
+\startbuffer[3a]
+\startMPcode
+ draw lmt_chart_histogram [
+ samples = { { 1, 4, 3, 2, 5, 7, 6 } },
+ percentage = true,
+ cumulative = true,
+ trace = true,
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[3b]
+\startMPcode
+ draw lmt_chart_histogram [
+ samples = {
+ { 1, 4, 3, 2, 5, 7, 6 },
+ { 1, 2, 3, 4, 5, 6, 7 }
+ },
+ background = "lightgray",
+ trace = true,
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[3a][option=TEX]
+
+and one with two datasets:
+
+\typebuffer[3b][option=TEX]
+
+\startplacefigure[reference=chart:3]
+ \startcombination
+ {\getbuffer[3a]} {}
+ {\getbuffer[3b]} {}
+ \stopcombination
+\stopplacefigure
+
+\startbuffer[4]
+\startMPpage[offset=5mm]
+ draw lmt_chart_histogram [
+ samples = {
+ { 1, 4, 3, 2, 5, 7, 6 },
+ { 1, 2, 3, 4, 5, 6, 7 }
+ },
+ percentage = true,
+ cumulative = true,
+ showlabels = false,
+ backgroundcolor = "lightgray",
+ ] ;
+\stopMPpage
+\stopbuffer
+
+A cumulative variant is shown in \in {figure} [chart:4] where we also add a
+background (color).
+
+\typebuffer[4][option=TEX]
+
+\startplacefigure[reference=chart:4]
+ \getbuffer[4]
+\stopplacefigure
+
+A different way of using colors is shown in \in {figure} [chart:5] where each
+sample gets its own (same) color.
+
+\startbuffer[5]
+\startMPcode
+ draw lmt_chart_histogram [
+ samples = {
+ { 1, 4, 3, 2, 5, 7, 6 },
+ { 1, 2, 3, 4, 5, 6, 7 }
+ },
+ percentage = true,
+ cumulative = true,
+ showlabels = false,
+ background = "lightgray",
+ colormode = "local",
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[5][option=TEX]
+
+\startplacefigure[reference=chart:5]
+ \getbuffer[5]
+\stopplacefigure
+
+As with pie charts you can add labels and a legend:
+
+\startbuffer[6a]
+\startMPcode
+ draw lmt_chart_histogram [
+ height = 6cm,
+ samples = { { 1, 4, 3, 2, 5, 7, 6 } },
+ percentage = true,
+ cumulative = true,
+ trace = true,
+ labelstyle = "ttxx",
+ labelanchor = "top",
+ labelcolor = "white",
+ backgroundcolor = "middlegray",
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[6a][option=TEX]
+
+The previous and next examples are shown in \in {figure} [chart:6]. The height
+specified here concerns the graphic and excludes the labels,
+
+\startbuffer[6b]
+\startMPcode
+ draw lmt_chart_histogram [
+ height = 6cm,
+ width = 10mm,
+ samples = { { 1, 4, 3, 2, 5, 7, 6 } },
+ trace = true,
+ maximum = 7.5,
+ linewidth = 1mm,
+ originsize = 0,
+ labelanchor = "bot",
+ labelcolor = "black"
+ labelstyle = "bfxx"
+ legendstyle = "tfxx",
+ labelstrut = "yes",
+ legend = {
+ "first", "second", "third", "fourth",
+ "fifth", "sixths", "sevenths"
+ }
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[6b][option=TEX]
+
+\startplacefigure[reference=chart:6]
+ \startcombination
+ {\getbuffer[6a]} {}
+ {\getbuffer[6b]} {}
+ \stopcombination
+\stopplacefigure
+
+The third category concerns bar charts that run horizontal. Again we see similar
+options driving the rendering (\in {figure} [chart:7]).
+
+\startbuffer[7a]
+\startMPcode
+ draw lmt_chart_bar [
+ samples = { { 1, 4, 3, 2, 5, 7, 6 } },
+ percentage = true,
+ cumulative = true,
+ trace = true,
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[7a][option=TEX]
+
+\startbuffer[7b]
+\startMPcode
+ draw lmt_chart_bar [
+ samples = { { 1, 4, 3, 2, 5, 7, 6 } },
+ percentage = true,
+ cumulative = true,
+ showlabels = false,
+ backgroundcolor = "lightgray",
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[7b][option=TEX]
+
+Determining the offset of labels is manual work:
+
+\startbuffer[7c]
+\startMPcode
+draw lmt_chart_bar [
+ width = 4cm,
+ height = 5mm,
+ samples = { { 1, 4, 3, 2, 5, 7, 6 } },
+ percentage = true,
+ cumulative = true,
+ trace = true,
+ labelcolor = "white",
+ labelstyle = "ttxx",
+ labelanchor = "rt",
+ labeloffset = .25EmWidth,
+ backgroundcolor = "middlegray",
+] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[7c][option=TEX]
+
+\startplacefigure[reference=chart:7]
+ \startcombination[3*1]
+ {\getbuffer[7a]} {}
+ {\getbuffer[7b]} {}
+ {\getbuffer[7c]} {}
+ \stopcombination
+\stopplacefigure
+
+Here is one with a legend (rendered in \in {figure} [chart:8]):
+
+\startbuffer[8]
+\startMPcode
+draw lmt_chart_bar [
+ width = 8cm,
+ height = 10mm,
+ samples = { { 1, 4, 3, 2, 5, 7, 6 } },
+ trace = true,
+ maximum = 7.5,
+ linewidth = 1mm,
+ originsize = 0,
+ labelanchor = "lft",
+ labelcolor = "black"
+ labelstyle = "bfxx"
+ legendstyle = "tfxx",
+ labelstrut = "yes",
+ legend = {
+ "first", "second", "third", "fourth",
+ "fifth", "sixths", "sevenths"
+ }
+] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[8][option=TEX]
+
+\startplacefigure[reference=chart:8]
+ \getbuffer[8]
+\stopplacefigure
+
+You can have labels per dataset as well as draw multiple datasets in
+one image, see \in {figure} [chart:9]:
+
+\startbuffer[9]
+\startMPcode
+ draw lmt_chart_bar [
+ samples = {
+ { 1, 4, 3, 2, 5, 7, 6 },
+ { 3, 2, 5, 7, 5, 6, 1 }
+ },
+ labels = {
+ { "a1", "b1", "c1", "d1", "e1", "f1", "g1" },
+ { "a2", "b2", "c2", "d2", "e2", "f2", "g2" }
+ },
+ labeloffset = -EmWidth,
+ labelanchor = "center",
+ labelstyle = "ttxx",
+ trace = true,
+ center = true,
+ ] ;
+
+ draw lmt_chart_bar [
+ samples = {
+ { 1, 4, 3, 2, 5, 7, 6 }
+ },
+ labels = {
+ { "a", "b", "c", "d", "e", "f", "g" }
+ },
+ labeloffset = -EmWidth,
+ labelanchor = "center",
+ trace = true,
+ center = true,
+ ] shifted (10cm,0) ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[9][option=TEX]
+
+\startplacefigure[reference=chart:9]
+ \getbuffer[9]
+\stopplacefigure
+
+\starttabulate[|T|T|T|p|]
+\FL
+\BC name \BC type \BC default \BC comment \NC \NR
+\ML
+\NC originsize \NC numeric \NC 1mm \NC \NC \NR
+\NC trace \NC boolean \NC false \NC \NC \NR
+\NC showlabels \NC boolean \NC true \NC \NC \NR
+\NC center \NC boolean \NC false \NC \NC \NR
+\ML
+\NC samples \NC list \NC \NC \NC \NR
+\NC
+\NC cumulative \NC boolean \NC false \NC \NC \NR
+\NC percentage \NC boolean \NC false \NC \NC \NR
+\NC maximum \NC numeric \NC 0 \NC \NC \NR
+\NC distance \NC numeric \NC 1mm \NC \NC \NR
+\ML
+\NC labels \NC list \NC \NC \NC \NR
+\NC labelstyle \NC string \NC \NC \NC \NR
+\NC labelformat \NC string \NC \NC \NC \NR
+\NC labelstrut \NC string \NC auto \NC \NC \NR
+\NC labelanchor \NC string \NC \NC \NC \NR
+\NC labeloffset \NC numeric \NC 0 \NC \NC \NR
+\NC labelfraction \NC numeric \NC 0.8 \NC \NC \NR
+\NC labelcolor \NC string \NC \NC \NC \NR
+\ML
+\NC backgroundcolor \NC string \NC \NC \NC \NR
+\NC drawcolor \NC string \NC white \NC \NC \NR
+\NC fillcolors \NC list \NC \NC primary (dark) colors \NC \NR
+\NC colormode \NC string \NC global \NC \NC or \type {local} \NC \NR
+\ML
+\NC linewidth \NC numeric \NC .25mm \NC \NC \NR
+\ML
+\NC legendcolor \NC string \NC \NC \NC \NR
+\NC legendstyle \NC string \NC \NC \NC \NR
+\NC legend \NC list \NC \NC \NC \NR
+\LL
+\stoptabulate
+
+Pie charts have:
+
+\starttabulate[|T|T|]
+\FL
+\BC name \BC default \NC \NR
+\ML
+\NC height \NC 5cm \NC \NR
+\NC width \NC 5mm \NC \NR
+\NC labelanchor \NC \NC \NR
+\NC labeloffset \NC 0 \NC \NR
+\NC labelstrut \NC no \NC \NR
+\LL
+\stoptabulate
+
+Histograms come with:
+
+\starttabulate[|T|T|]
+\FL
+\BC name \BC default \NC \NR
+\ML
+\NC height \NC 5cm \NC \NR
+\NC width \NC 5mm \NC \NR
+\NC labelanchor \NC bot \NC \NR
+\NC labeloffset \NC 1mm \NC \NR
+\NC labelstrut \NC auto \NC \NR
+\LL
+\stoptabulate
+
+Bar charts use:
+
+\starttabulate[|T|T|]
+\FL
+\BC name \BC default \NC \NR
+\ML
+\NC height \NC 5cm \NC \NR
+\NC width \NC 5mm \NC \NR
+\NC labelanchor \NC lft \NC \NR
+\NC labeloffset \NC 1mm \NC \NR
+\NC labelstrut \NC no \NC \NR
+\LL
+\stoptabulate
+
+\stopchapter
+
+\stopcomponent
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-contents.tex b/doc/context/sources/general/manuals/luametafun/luametafun-contents.tex
new file mode 100644
index 000000000..73800ba8b
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-contents.tex
@@ -0,0 +1,11 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-contents
+
+\starttitle[title={Contents}]
+ \placelist[chapter]
+\stoptitle
+
+\stopcomponent
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-contour.tex b/doc/context/sources/general/manuals/luametafun/luametafun-contour.tex
new file mode 100644
index 000000000..11eca8b7a
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-contour.tex
@@ -0,0 +1,771 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-contour
+
+\startchapter[title={Contour}]
+
+This feature started out as experiment triggered by a request on the mailing
+list. In the end it was a nice exploration of what is possible with a bit of
+\LUA. In a sense it is more subsystem than a simple \METAPOST\ macro because
+quite some \LUA\ code is involved and more might be used in the future. It's part
+of the fun.
+
+A contour is a line through equivalent values $z$ that result from applying a
+function to two variables $x$ and $y$. There is quite a bit of analysis needed
+to get these lines. In \METAFUN\ we currently support three methods for generating
+a colorful background and three for putting lines on top:
+
+One solution is to use the the isolines and isobands methods are described on the
+marching squares page of wikipedia:
+
+\starttyping
+https://en.wikipedia.org/wiki/Marching_squares
+\stoptyping
+
+This method is relative efficient as we don't do much optimization, simply
+because it takes time and the gain is not that much relevant. Because we support
+filling of multiple curves in one go, we get efficient paths anyway without side
+effects that normally can occur from many small paths alongside. In these days of
+multi megabyte movies and sound clips a request of making a \PDF\ file small is kind
+of strange anyway. In practice the penalty is not that large.
+
+As background we can use a bitmap. This method is also quite efficient because we
+use indexed colors which results in a very good compression. We use a simple
+mapping on a range of values.
+
+A third method is derived from the one that is distributed as \CCODE\ source
+file at:
+
+\starttyping
+https://physiology.arizona.edu/people/secomb/contours
+https://github.com/secomb/GreensV4
+\stoptyping
+
+We can create a background image, which uses a sequence of closed curves
+\footnote {I have to figure out how to improve it a bit so that multiple path
+don't get connected.}. It can also provide two variants of lines around the
+contours (we tag them shape and shade). It's all a matter of taste. In the
+meantime I managed to optimize the code a bit and I suppose that when I buy a new
+computer (the code was developed on an 8 year old machine) performance is
+probably acceptable.
+
+In order of useability you can think of isoband (band) with isolines (cell),
+bitmap (bitmap) with isolines (cell) and finally shapes (shape) with edges
+(edge). But let's start with a couple of examples.
+
+\startbuffer[1]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = 0, xmax = 4*pi, xstep = .05,
+ ymin = -6, ymax = 6, ystep = .05,
+
+ levels = 7,
+ height = 5cm,
+ preamble = "local sin, cos = math.sin, math.cos",
+ function = "cos(x) + sin(y)",
+ background = "bitmap",
+ foreground = "edge",
+ linewidth = 1/2,
+ cache = true,
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\startplacefigure[reference=contour:1]
+ \getbuffer[1]
+\stopplacefigure
+
+\typebuffer[1][option=TEX]
+
+In \in {figure} [contour:1] we see the result. There is a in this case black and
+white image generated and on top of that we see lines. The step determines the
+resolution of the image. In practice using a bitmap is quite okay and also rather
+efficient: we use an indexed colorspace and, as already was mentioned, because
+the number of colors is limited such an image compresses well. A different
+rendering is seen in \in {figure} [contour:2] where we use the shape method for
+the background. That method creates outlines but is much slower, and when you use
+a high resolution (small step) it can take quite a while to identify the shapes.
+This is why we set the cache flag.
+
+\startbuffer[2]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = 0, xmax = 4*pi, xstep = .10,
+ ymin = -6, ymax = 6, ystep = .10,
+
+ levels = 7,
+ preamble = "local sin, cos = math.sin, math.cos",
+ function = "cos(x) - sin(y)",
+ background = "shape",
+ foreground = "shape",
+ linewidth = 1/2,
+ cache = true,
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[2][option=TEX]
+
+\startplacefigure[reference=contour:2]
+ \getbuffer[2]
+\stopplacefigure
+
+We mentioned colorspace but haven't seen any color yet, so let's set some in \in
+{figure} [contour:3]. Two variants are shown: a background \type {shape} with
+foreground \type {shape} and a background \type {bitmap} with a foreground \type
+{edge}. The bitmap renders quite fast, definitely when we compare with the shape,
+while the quality is as good at this size.
+
+\startbuffer[3a]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = -10, xmax = 10, xstep = .1,
+ ymin = -10, ymax = 10, ystep = .1,
+
+ levels = 10,
+ height = 7cm,
+ color = "shade({1/2,1/2,0},{0,0,1/2})",
+ function = "x^2 + y^2",
+ background = "shape",
+ foreground = "shape",
+ linewidth = 1/2,
+ cache = true,
+ ] xsized .45TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[3b]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = -10, xmax = 10, xstep = .1,
+ ymin = -10, ymax = 10, ystep = .1,
+
+ levels = 10,
+ height = 7cm,
+ color = "shade({1/2,0,0},{0,0,1/2})",
+ function = "x^2 + y^2",
+ background = "bitmap",
+ foreground = "edge",
+ linewidth = 1/2,
+ cache = true,
+ ] xsized .45TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[3a][option=TEX]
+
+\startplacefigure[reference=contour:3]
+ \startcombination
+ {\getbuffer[3a]} {\bf shape}
+ {\getbuffer[3b]} {\bf bitmap}
+ \stopcombination
+\stopplacefigure
+
+We use the \type {doublefun} instance because we need to be sure that we don't
+run into issues with scaled numbers, the default model in \METAPOST. The
+function that gets passed is {\em not} using \METAPOST\ but \LUA, so basically
+you can do very complex things. Here we directly pass code, but you can for
+instance also do this:
+
+\starttyping[option=TEX]
+\startluacode
+ function document.MyContourA(x,y)
+ return x^2 + y^2
+ end
+\stopluacode
+\stoptyping
+
+and then \type {function = "document.MyContourA(x,y)"}. As long as the function
+returns a valid number we're okay. When you pass code directly you can use the
+\type {preamble} key to set local shortcuts. In the previous examples we took
+\type {sin} and \type {cos} from the math library but you can also roll out your
+own functions and|/|or use the more elaborate \type {xmath} library. The color
+parameter is also a function, one that returns one or three arguments. In the
+next example we use \type {lin} to calculate a fraction of the current level and
+total number of levels.
+
+\startbuffer[4a]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = -3, xmax = 3, xstep = .01,
+ ymin = -1, ymax = 1, ystep = .01,
+
+ levels = 10,
+ default = .5,
+ height = 5cm,
+ function = "x^2 + y^2 + x + y/2",
+ color = "lin(l), 0, 1/2",
+ background = "bitmap"
+ foreground = "none",
+ cache = true,
+ ] xsized TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[4a][option=TEX]
+
+\startplacefigure[reference=contour:4a]
+ \getbuffer[4a]
+\stopplacefigure
+
+Instead of a bitmap we can use an isoband, which boils down to a set of tiny
+shapes that make up a bigger one. This is shown in \in {figure} [contour:4b].
+
+\startbuffer[4b]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = -3, xmax = 3, xstep = .01,
+ ymin = -1, ymax = 1, ystep = .01,
+
+ levels = 10,
+ default = .5,
+ height = 5cm,
+ function = "x^2 + y^2 + x + y/2",
+ color = "lin(l), 1/2, 0",
+ background = "band",
+ foreground = "none",
+ cache = true,
+ ] xsized TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[4b][option=TEX]
+
+\startplacefigure[reference=contour:4b]
+ \getbuffer[4b]
+\stopplacefigure
+
+You can draw several functions and see where they overlap:
+
+\startbuffer[5]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = -pi, xmax = 4*pi, xstep = .1,
+ ymin = -3, ymax = 3, ystep = .1,
+
+ range = { -.1, .1 },
+ preamble = "local sin, cos = math.sin, math.cos",
+ functions = {
+ "sin(x) + sin(y)", "sin(x) + cos(y)",
+ "cos(x) + sin(y)", "cos(x) + cos(y)"
+ },
+ background = "bitmap",
+ linecolor = "black",
+ linewidth = 1/10,
+ color = "shade({1,1,0},{0,0,1})"
+ cache = true,
+ ] xsized TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[5][option=TEX]
+
+\startplacefigure[reference=contour:5]
+ \getbuffer[5]
+\stopplacefigure
+
+The range determines the $z$ value(s) that we take into account. You can also
+pass a list of colors to be used. In \in {figure} [contour:6] this is
+demonstrated. There we also show a variant foreground \type {cell}, which uses a
+bit different method for calculating the edges. \footnote {This a bit of a
+playground: more variants might show up in due time.}
+
+\startbuffer[6]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = -2*pi, xmax = 2*pi, xstep = .01,
+ ymin = -3, ymax = 3, ystep = .01,
+
+ range = { -.1, .1 },
+ preamble = "local sin, cos = math.sin, math.cos",
+ functions = { "sin(x) + sin(y)", "sin(x) + cos(y)" },
+ background = "bitmap",
+ foreground = "cell",
+ linecolor = "white",
+ linewidth = 1/10,
+ colors = { (1/2,1/2,1/2), red, green, blue }
+ level = 3,
+ linewidth = 6,
+ cache = true,
+ ] xsized TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[6][option=TEX]
+
+Here the number of levels depends on the number of functions as each can overlap
+with another; for instance the outcome of two functions can overlap or not which
+means 3 cases, and with a value not being seen that gives 4 different cases.
+
+\startplacefigure[reference=contour:6]
+ \getbuffer[6]
+\stopplacefigure
+
+\startbuffer[7]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = -2*pi, xmax = 2*pi, xstep = .01,
+ ymin = -3, ymax = 3, ystep = .01,
+
+ range = { -.1, .1 },
+ preamble = "local sin, cos = math.sin, math.cos",
+ functions = {
+ "sin(x) + sin(y)",
+ "sin(x) + cos(y)",
+ "cos(x) + sin(y)",
+ "cos(x) + cos(y)"
+ },
+ background = "bitmap",
+ foreground = "none",
+ level = 3,
+ color = "shade({2/3,0,0},{2/3,1,2/3})"
+ cache = true,
+ ] xsized TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[7][option=TEX]
+
+Of course one can wonder how useful showing many functions but it can give nice
+pictures, as shown in \in {figure} [contour:7].
+
+\startplacefigure[reference=contour:7]
+ \getbuffer[7]
+\stopplacefigure
+
+\startbuffer[8]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = -2*pi, xmax = 2*pi, xstep = .01,
+ ymin = -3, ymax = 3, ystep = .01,
+
+ range = { -.3, .3 },
+ preamble = "local sin, cos = math.sin, math.cos",
+ functions = {
+ "sin(x) + sin(y)",
+ "sin(x) + cos(y)",
+ "cos(x) + sin(y)",
+ "cos(x) + cos(y)"
+ },
+ background = "bitmap",
+ foreground = "none",
+ level = 3,
+ color = "shade({1,0,0},{0,1,0})"
+ cache = true,
+ ] xsized TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[8][option=TEX]
+
+We can enlargen the window, which is demonstrated in \in {figure} [contour:8]. I
+suppose that such images only make sense in educational settings.
+
+\startplacefigure[reference=contour:8]
+ \getbuffer[8]
+\stopplacefigure
+
+% \startbuffer[9a]
+% \startMPcode{doublefun}
+% draw lmt_contour [
+% xmin = -10, xmax = 10, xstep = 1,
+% ymin = -10, ymax = 10, ystep = 1,
+% function = "math.random(1,3)", levels = 3,
+% linecolor = "white", linewidth = 1/10,
+% width = .3TextWidth, legend = "none",
+% color = "shade({1/2,1/2,0},{0,0,1})"
+%
+% background = "bitmap", foreground = "edge",
+% ] ;
+% \stopMPcode
+% \stopbuffer
+%
+% \typebuffer[9a][option=TEX]
+%
+% \startbuffer[9b]
+% \startMPcode{doublefun}
+% draw lmt_contour [
+% xmin = -10, xmax = 10, xstep = 1,
+% ymin = -10, ymax = 10, ystep = 1,
+% function = "math.random(1,3)", levels = 3,
+% linecolor = "white", linewidth = 1/10,
+% width = .3TextWidth, legend = "none",
+% color = "shade({1/2,1/2,0},{0,0,1/2})"
+%
+% background = "bitmap", foreground = "cell",
+% ] ;
+% \stopMPcode
+% \stopbuffer
+%
+% \startbuffer[9c]
+% \startMPcode{doublefun}
+% draw lmt_contour [
+% xmin = -10, xmax = 10, xstep = 1,
+% ymin = -10, ymax = 10, ystep = 1,
+% function = "math.random(1,3)", levels = 3,
+% linecolor = "white", linewidth = 1/10,
+% width = .3TextWidth, legend = "none",
+% color = "shade({1/2,1/2,0},{0,0,1})"
+%
+% background = "bitmap", foreground = "none",
+% ] ;
+% \stopMPcode
+% \stopbuffer
+%
+% \startbuffer[9d]
+% \startMPcode{doublefun}
+% draw lmt_contour [
+% xmin = -10, xmax = 10, xstep = 1,
+% ymin = -10, ymax = 10, ystep = 1,
+% function = "math.random(1,3)", levels = 3,
+% linecolor = "white", linewidth = 1/10,
+% width = .3TextWidth, legend = "none",
+% color = "shade({1/2,1/2,0},{0,0,1})"
+%
+% background = "shape", foreground = "shape",
+% ] ;
+% \stopMPcode
+% \stopbuffer
+%
+% \startbuffer[9e]
+% \startMPcode{doublefun}
+% draw lmt_contour [
+% xmin = -10, xmax = 10, xstep = 1,
+% ymin = -10, ymax = 10, ystep = 1,
+% function = "math.random(1,3)", levels = 3,
+% linecolor = "white", linewidth = 1/10,
+% width = .3TextWidth, legend = "none",
+% color = "shade({1/2,1/2,0},{0,0,1})"
+%
+% background = "shape", foreground = "edge",
+% ] ;
+% \stopMPcode
+% \stopbuffer
+%
+% \startbuffer[9f]
+% \startMPcode{doublefun}
+% draw lmt_contour [
+% xmin = -10, xmax = 10, xstep = 1,
+% ymin = -10, ymax = 10, ystep = 1,
+% function = "math.random(1,3)", levels = 3,
+% linecolor = "white", linewidth = 1/10,
+% width = .3TextWidth, legend = "none",
+% color = "shade({1/2,1/2,0},{0,0,1})"
+%
+% background = "shape", foreground = "none",
+% ] ;
+% \stopMPcode
+% \stopbuffer
+%
+% \startbuffer[9g]
+% \startMPcode{doublefun}
+% draw lmt_contour [
+% xmin = -10, xmax = 10, xstep = 1,
+% ymin = -10, ymax = 10, ystep = 1,
+% function = "math.random(1,3)", levels = 3,
+% linecolor = "white", linewidth = 1/10,
+% width = .3TextWidth, legend = "none",
+% color = "shade({1/2,1/2,0},{0,0,1})"
+%
+% background = "band", foreground = "edge",
+% ] ;
+% \stopMPcode
+% \stopbuffer
+%
+% \startbuffer[9h]
+% \startMPcode{doublefun}
+% draw lmt_contour [
+% xmin = -10, xmax = 10, xstep = 1,
+% ymin = -10, ymax = 10, ystep = 1,
+% function = "math.random(1,3)", levels = 3,
+% linecolor = "white", linewidth = 1/10,
+% width = .3TextWidth, legend = "none",
+% color = "shade({1/2,1/2,0},{0,0,1})"
+%
+% background = "band", foreground = "cell",
+% ] ;
+% \stopMPcode
+% \stopbuffer
+%
+% \startbuffer[9i]
+% \startMPcode{doublefun}
+% draw lmt_contour [
+% xmin = -10, xmax = 10, xstep = 1,
+% ymin = -10, ymax = 10, ystep = 1,
+% function = "math.random(1,3)", levels = 3,
+% linecolor = "white", linewidth = 1/10,
+% width = .3TextWidth, legend = "none",
+% color = "shade({1/2,1/2,0},{0,0,1})"
+%
+% background = "band", foreground = "none",
+% ] ;
+% \stopMPcode
+% \stopbuffer
+%
+% In \in {figure} [contour:9] we see that using the shape option doesn't work out
+% too well here, which again demonstrates that using the bitmap method is not that
+% bad. In that example we use random numbers, just to show the erratic behavior. In
+% \in {figure} [contour:10] a more sane image is used. The band and bitmap examples
+% are generated quite fast so no caching is used there. We only show one definition:
+%
+% \typebuffer[9i][option=TEX]
+%
+% \startplacefigure[reference=contour:9]
+% \startcombination[3*3]
+% {\pushrandomseed\setrandomseed{1}\getbuffer[9a]\poprandomseed} {\bf bitmap edge}
+% {\pushrandomseed\setrandomseed{1}\getbuffer[9b]\poprandomseed} {\bf bitmap cell}
+% {\pushrandomseed\setrandomseed{1}\getbuffer[9c]\poprandomseed} {\bf bitmap none}
+% {\pushrandomseed\setrandomseed{1}\getbuffer[9d]\poprandomseed} {\bf shape shape}
+% {\pushrandomseed\setrandomseed{1}\getbuffer[9e]\poprandomseed} {\bf shape edge}
+% {\pushrandomseed\setrandomseed{1}\getbuffer[9f]\poprandomseed} {\bf shape none}
+% {\pushrandomseed\setrandomseed{1}\getbuffer[9g]\poprandomseed} {\bf band edge}
+% {\pushrandomseed\setrandomseed{1}\getbuffer[9h]\poprandomseed} {\bf band cell}
+% {\pushrandomseed\setrandomseed{1}\getbuffer[9i]\poprandomseed} {\bf band none}
+% \stopcombination
+% \stopplacefigure
+
+In \in {figure} [contour:10] we see different combinations of backgrounds (in color)
+and foregrounds (edges) in action.
+
+\startbuffer[10a]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = 0, xmax = 4*pi, xstep = 0,
+ ymin = -6, ymax = 6, ystep = 0,
+
+ levels = 5, legend = false, linewidth = 1/2,
+
+ preamble = "local sin, cos = math.sin, math.cos",
+ function = "cos(x) - sin(y)",
+ color = "shade({1/2,0,0},{0,0,1/2})",
+
+ background = "bitmap", foreground = "edge",
+ ] xsized .3TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[10b]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = 0, xmax = 4*pi, xstep = 0,
+ ymin = -6, ymax = 6, ystep = 0,
+
+ levels = 5, legend = false, linewidth = 1/2,
+
+ preamble = "local sin, cos = math.sin, math.cos",
+ function = "cos(x) - sin(y)",
+ color = "shade({1/2,0,0},{0,0,1/2})",
+
+ background = "bitmap", foreground = "cell",
+ ] xsized .3TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[10c]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = 0, xmax = 4*pi, xstep = 0,
+ ymin = -6, ymax = 6, ystep = 0,
+
+ levels = 5, legend = false, linewidth = 1/2,
+
+ preamble = "local sin, cos = math.sin, math.cos",
+ function = "cos(x) - sin(y)",
+ color = "shade({1/2,0,0},{0,0,1/2})",
+
+ background = "bitmap", foreground = "none",
+ ] xsized .3TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[10d]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = 0, xmax = 4*pi, xstep = 0,
+ ymin = -6, ymax = 6, ystep = 0,
+
+ levels = 5, legend = false, linewidth = 1/2,
+
+ preamble = "local sin, cos = math.sin, math.cos",
+ function = "cos(x) - sin(y)",
+ color = "shade({1/2,0,0},{0,0,1/2})",
+
+ background = "shape", foreground = "shape", cache = true,
+ ] xsized .3TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[10e]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = 0, xmax = 4*pi, xstep = 0,
+ ymin = -6, ymax = 6, ystep = 0,
+
+ levels = 5, legend = false, linewidth = 1/2,
+
+ preamble = "local sin, cos = math.sin, math.cos",
+ function = "cos(x) - sin(y)",
+ color = "shade({1/2,0,0},{0,0,1/2})",
+
+ background = "shape", foreground = "edge", cache = true,
+ ] xsized .3TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[10f]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = 0, xmax = 4*pi, xstep = 0,
+ ymin = -6, ymax = 6, ystep = 0,
+
+ levels = 5, legend = false, linewidth = 1/2,
+
+ preamble = "local sin, cos = math.sin, math.cos",
+ function = "cos(x) - sin(y)",
+ color = "shade({1/2,0,0},{0,0,1/2})",
+
+ background = "shape", foreground = "none", cache = true,
+ ] xsized .3TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[10g]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = 0, xmax = 4*pi, xstep = 0,
+ ymin = -6, ymax = 6, ystep = 0,
+
+ levels = 5, legend = false, linewidth = 1/2,
+
+ preamble = "local sin, cos = math.sin, math.cos",
+ function = "cos(x) - sin(y)",
+ color = "shade({1/2,0,0},{0,0,1/2})",
+
+ background = "band", foreground = "edge",
+ ] xsized .3TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[10h]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = 0, xmax = 4*pi, xstep = 0,
+ ymin = -6, ymax = 6, ystep = 0,
+
+ levels = 5, legend = false, linewidth = 1/2,
+
+ preamble = "local sin, cos = math.sin, math.cos",
+ function = "cos(x) - sin(y)",
+ color = "shade({1/2,0,0},{0,0,1/2})",
+
+ background = "band", foreground = "cell",
+ ] xsized .3TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[10i]
+\startMPcode{doublefun}
+ draw lmt_contour [
+ xmin = 0, xmax = 4*pi, xstep = 0,
+ ymin = -6, ymax = 6, ystep = 0,
+
+ levels = 5, legend = false, linewidth = 1/2,
+
+ preamble = "local sin, cos = math.sin, math.cos",
+ function = "cos(x) - sin(y)",
+ color = "shade({1/2,0,0},{0,0,1/2})",
+
+ background = "band", foreground = "none",
+ ] xsized .3TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[10b][option=TEX]
+
+% \page
+
+There are quite some settings. Some deal with the background, some with the
+foreground and quite some deal with the legend.
+
+\starttabulate[|T|T|T|p|]
+\FL
+\BC name \BC type \BC default \BC comment \NC \NR
+\ML
+\NC xmin \NC numeric \NC 0 \NC needs to be set \NC \NR
+\NC xmax \NC numeric \NC 0 \NC needs to be set \NC \NR
+\NC ymin \NC numeric \NC 0 \NC needs to be set \NC \NR
+\NC ymax \NC numeric \NC 0 \NC needs to be set \NC \NR
+\NC xstep \NC numeric \NC 0 \NC auto 1/200 when zero \NC \NR
+\NC ystep \NC numeric \NC 0 \NC auto 1/200 when zero \NC \NR
+\NC checkresult \NC boolean \NC false \NC checks for overflow and NaN \NC \NR
+\NC defaultnan \NC numeric \NC 0 \NC the value to be used when NaN \NC \NR
+\NC defaultinf \NC numeric \NC 0 \NC the value to be used when overflow \NC \NR
+\ML
+\NC levels \NC numeric \NC 10 \NC number of different levels to show \NC \NR
+\NC level \NC numeric \NC \NC only show this level (foreground) \NC \NR
+\ML
+\NC preamble \NC string \NC \NC shortcuts \NC \NR
+\NC function \NC string \NC x + y \NC the result z value \NC \NR
+\NC functions \NC list \NC \NC multiple functions (overlapping levels) \NC \NR
+\NC color \NC string \NC lin(l) \NC the result color value for level l (1 or 3 values) \NC \NR
+\NC colors \NC numeric \NC \NC used when set \NC \NR
+\ML
+\NC background \NC string \NC bitmap \NC band, bitmap, shape \NC \NR
+\NC foreground \NC string \NC auto \NC cell, edge, shape auto \NC \NR
+\ML
+\NC linewidth \NC numeric \NC .25 \NC \NC \NR
+%NC backgroundcolor \NC string \NC black \NC \NC \NR
+\NC linecolor \NC string \NC gray \NC \NC \NR
+\ML
+\NC width \NC numeric \NC 0 \NC automatic when zero \NC \NR
+\NC height \NC numeric \NC 0 \NC automatic when zero \NC \NR
+\ML
+\NC trace \NC boolean \NC false \NC \NC \NR
+\ML
+\NC legend \NC string \NC all \NC x y z function range all \NC \NR
+\NC legendheight \NC numeric \NC LineHeight \NC \NC \NR
+\NC legendwidth \NC numeric \NC LineHeight \NC \NC \NR
+\NC legendgap \NC numeric \NC 0 \NC \NC \NR
+\NC legenddistance \NC numeric \NC EmWidth \NC \NC \NR
+\NC textdistance \NC numeric \NC 2EmWidth/3 \NC \NC \NR
+\NC functiondistance \NC numeric \NC ExHeight \NC \NC \NR
+\NC functionstyle \NC string \NC \NC \CONTEXT\ style name \NC \NR
+\NC xformat \NC string \NC @0.2N \NC number format template \NC \NR
+\NC yformat \NC string \NC @0.2N \NC number format template \NC \NR
+\NC zformat \NC string \NC @0.2N \NC number format template \NC \NR
+\NC xstyle \NC string \NC \NC \CONTEXT\ style name \NC \NR
+\NC ystyle \NC string \NC \NC \CONTEXT\ style name \NC \NR
+\NC zstyle \NC string \NC \NC \CONTEXT\ style name \NC \NR
+\ML
+\NC axisdistance \NC numeric \NC ExHeight \NC \NC \NR
+\NC axislinewidth \NC numeric \NC .25 \NC \NC \NR
+\NC axisoffset \NC numeric \NC ExHeight/4 \NC \NC \NR
+\NC axiscolor \NC string \NC black \NC \NC \NR
+\NC ticklength \NC numeric \NC ExHeight \NC \NC \NR
+\ML
+\NC xtick \NC numeric \NC 5 \NC \NC \NR
+\NC ytick \NC numeric \NC 5 \NC \NC \NR
+\NC xlabel \NC numeric \NC 5 \NC \NC \NR
+\NC ylabel \NC numeric \NC 5 \NC \NC \NR
+\LL
+\stoptabulate
+
+\startplacefigure[reference=contour:10]
+ \startcombination[3*3]
+ {\getbuffer[10a]} {\bf bitmap edge}
+ {\getbuffer[10b]} {\bf bitmap cell}
+ {\getbuffer[10c]} {\bf bitmap none}
+ {\getbuffer[10d]} {\bf shape shape}
+ {\getbuffer[10e]} {\bf shape edge}
+ {\getbuffer[10f]} {\bf shape none}
+ {\getbuffer[10g]} {\bf band edge}
+ {\getbuffer[10h]} {\bf band cell}
+ {\getbuffer[10i]} {\bf band none}
+ \stopcombination
+\stopplacefigure
+
+\stopchapter
+
+\stopcomponent
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-followtext.tex b/doc/context/sources/general/manuals/luametafun/luametafun-followtext.tex
new file mode 100644
index 000000000..079131565
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-followtext.tex
@@ -0,0 +1,124 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-followtext
+
+\startchapter[title={Followtext}]
+
+Typesetting text along a path started as a demo if communication between \TEX\ and
+\METAPOST\ in the early days of \METAFUN. In the meantime the implementation has
+been modernized a few times and the current implementation feels okay, especially
+now that we have a better user interface. Here is an example:
+
+\startbuffer[1a]
+\startMPcode{doublefun}
+ draw lmt_followtext [
+ text = "How well does it work {\bf 1}! ",
+ path = fullcircle scaled 4cm,
+ trace = true,
+ spread = true,
+ ] ysized 5cm ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[1a][option=TEX]
+
+Here is the same example but with the text in the reverse order. The results of both examples
+are shown in \in {figure} [followtext:1].
+
+\startbuffer[1b]
+\startMPcode{doublefun}
+ draw lmt_followtext [
+ text = "How well does it work {\bf 2}! ",
+ path = fullcircle scaled 4cm,
+ trace = true,
+ spread = false,
+ reverse = true,
+ ] ysized 5cm ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[1b][option=TEX]
+
+\startplacefigure[reference=followtext:1]
+ \startcombination[2*2]
+ {\getbuffer[1a]} {}
+ {\getbuffer[1b]} {}
+ \stopcombination
+\stopplacefigure
+
+There are not that many options. One is \type {autoscale} which makes the shape
+and text match. \in {Figure} [followtext:2] shows what happens.
+
+\startbuffer[2a]
+\startMPcode{doublefun}
+ draw lmt_followtext [
+ text = "How well does it work {\bf 3}! ",
+ trace = true,
+ autoscaleup = "yes"
+ ] ysized 5cm ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[2a][option=TEX]
+
+\startbuffer[2b]
+\startMPcode{doublefun}
+ draw lmt_followtext [
+ text = "How well does it work {\bf 4}! ",
+ path = fullcircle scaled 2cm,
+ trace = true,
+ autoscaleup = "max"
+ ] ysized 5cm ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[2b][option=TEX]
+
+\startplacefigure[reference=followtext:2]
+ \startcombination[2*2]
+ {\getbuffer[2a]} {}
+ {\getbuffer[2b]} {}
+ \stopcombination
+\stopplacefigure
+
+You can use quite strange paths, like the one show in \in {figure}
+[followtext:3]. Watch the parenthesis around the path. this is really needed in
+order for the scanner to pick up the path (otherwise it sees a pair).
+
+\startbuffer[3]
+\startMPcode{doublefun}
+ draw lmt_followtext [
+ text = "\samplefile {zapf}",
+ path = ((3,0) .. (1,0) .. (5,0) .. (2,0) .. (4,0) .. (3,0)),
+ autoscaleup = "max"
+ ] xsized TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[3][option=TEX]
+
+\startplacefigure[reference=followtext:3]
+ \getbuffer[3]
+\stopplacefigure
+
+The small set of options is:
+
+\starttabulate[|T|T|T|p|]
+\FL
+\BC name \BC type \BC default \BC comment \NC \NR
+\ML
+\NC text \NC string \NC \NC \NC \NR
+\NC spread \NC string \NC true \NC \NC \NR
+\NC trace \NC numeric \NC false \NC \NC \NR
+\NC reverse \NC numeric \NC false \NC \NC \NR
+\NC autoscaleup \NC numeric \NC no \NC \NC \NR
+\NC autoscaledown \NC string \NC no \NC \NC \NR
+\NC path \NC string \NC (fullcircle) \NC \NC \NR
+\LL
+\stoptabulate
+
+\stopchapter
+
+\stopcomponent
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-function.tex b/doc/context/sources/general/manuals/luametafun/luametafun-function.tex
new file mode 100644
index 000000000..79fa97a21
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-function.tex
@@ -0,0 +1,296 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-function
+
+\startchapter[title={Function}]
+
+It is tempting to make helpers that can do a lot. However, that also means that
+we need to explain a lot. Instead it makes more sense to have specific helpers
+and just make another one when needed. Rendering functions falls into this
+category. At some point users will come up with specific cases that other users
+can use. Therefore, the solution presented here is not the ultimate answer. We
+start with a simple example:
+
+\startbuffer[1]
+\startMPcode{doublefun}
+ draw lmt_function [
+ xmin = 0, xmax = 20, xstep = .1,
+ ymin = -2, ymax = 2,
+
+ sx = 1mm, xsmall = 80, xlarge = 20,
+ sy = 4mm, ysmall = 40, ylarge = 4,
+
+ linewidth = .025mm, offset = .1mm,
+
+ code = "1.5 * math.sind (50 * x - 150)",
+ ]
+ xsized 8cm
+ ;
+\stopMPcode
+\stopbuffer
+
+\startplacefigure
+ \getbuffer[1]
+\stopplacefigure
+
+This image is defined as follows:
+
+\typebuffer[1][option=TEX]
+
+\startbuffer[2]
+\startMPcode{doublefun}
+ draw lmt_function [
+ xmin = 0, xmax = 20, xstep = .1,
+ ymin = -2, ymax = 2,
+
+ sx = 1mm, xsmall = 80, xlarge = 20,
+ sy = 4mm, ysmall = 40, ylarge = 4,
+
+ linewidth = .025mm, offset = .1mm,
+
+ xticks = "bottom",
+ yticks = "left",
+ xlabels = "nolimits",
+ ylabels = "yes",
+ code = "1.5 * math.sind (50 * x - 150)",
+ % frame = "ticks",
+ frame = "sticks",
+ ycaption = "\strut \rotate[rotation=90]{something vertical, using $\sin{x}$}",
+ xcaption = "\strut something horizontal",
+ functions = {
+ [ xmin = 1.0, xmax = 7.0, close = true, fillcolor = "darkred" ],
+ [ xmin = 7.0, xmax = 12.0, close = true, fillcolor = "darkgreen" ],
+ [ xmin = 12.0, xmax = 19.0, close = true, fillcolor = "darkblue" ],
+ [
+ drawcolor = "darkyellow",
+ drawsize = 2
+ ]
+ }
+ ]
+ xsized TextWidth
+ ;
+\stopMPcode
+\stopbuffer
+
+We can draw multiple functions in one go. The next sample split the drawing over
+a few ranges and is defined as follows; in \in {figure} [function:2] we see the
+result.
+
+\typebuffer[2][option=TEX]
+
+
+\startplacefigure[reference=function:2]
+ \getbuffer[2]
+\stopplacefigure
+
+Instead of the same function, we can draw different ones and when we use transparency
+we get nice results too.
+
+\startbuffer[3]
+\definecolor[MyColorR][r=.5,t=.5,a=1]
+\definecolor[MyColorG][g=.5,t=.5,a=1]
+\definecolor[MyColorB][b=.5,t=.5,a=1]
+
+\startMPcode{doublefun}
+ draw lmt_function [
+ xmin = 0, xmax = 20, xstep = .1,
+ ymin = -1, ymax = 1,
+
+ sx = 1mm, xsmall = 80, xlarge = 20,
+ sy = 4mm, ysmall = 40, ylarge = 4,
+
+ linewidth = .025mm, offset = .1mm,
+
+ functions = {
+ [
+ code = "math.sind (50 * x - 150)",
+ close = true,
+ fillcolor = "MyColorR"
+ ],
+ [
+ code = "math.cosd (50 * x - 150)",
+ close = true,
+ fillcolor = "MyColorB"
+ ]
+ },
+ ]
+ xsized TextWidth
+ ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[3][option=TEX]
+
+\startplacefigure[reference=function:3]
+ \getbuffer[3]
+\stopplacefigure
+
+It is important to choose a good step. In \in {figure} [function:4] we show 4
+variants and it is clear that in this case using straight line segments is better
+(or at least more efficient with small steps).
+
+\startbuffer[4a]
+\startMPcode{doublefun}
+ draw lmt_function [
+ xmin = 0, xmax = 10, xstep = .1,
+ ymin = -1, ymax = 1,
+
+ sx = 1mm, sy = 4mm,
+
+ linewidth = .025mm, offset = .1mm,
+
+ code = "math.sind (50 * x^2 - 150)",
+ shape = "curve"
+ ]
+ xsized .45TextWidth
+ ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[4b]
+\startMPcode{doublefun}
+ draw lmt_function [
+ xmin = 0, xmax = 10, xstep = .01,
+ ymin = -1, ymax = 1,
+
+ sx = 1mm, sy = 4mm,
+
+ linewidth = .025mm, offset = .1mm,
+
+ code = "math.sind (50 * x^2 - 150)",
+ shape = "curve"
+ ]
+ xsized .45TextWidth
+ ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[4c]
+\startMPcode{doublefun}
+ draw lmt_function [
+ xmin = 0, xmax = 10, xstep = .1,
+ ymin = -1, ymax = 1,
+
+ sx = 1mm, sy = 4mm,
+
+ linewidth = .025mm, offset = .1mm,
+
+ code = "math.sind (50 * x^2 - 150)",
+ shape = "line"
+ ]
+ xsized .45TextWidth
+ ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[4d]
+\startMPcode{doublefun}
+ draw lmt_function [
+ xmin = 0, xmax = 10, xstep = .01,
+ ymin = -1, ymax = 1,
+
+ sx = 1mm, sy = 4mm,
+
+ linewidth = .025mm, offset = .1mm,
+
+ code = "math.sind (50 * x^2 - 150)",
+ shape = "line"
+ ]
+ xsized .45TextWidth
+ ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[4a][option=TEX]
+
+\startplacefigure[reference=function:4]
+ \startcombination[2*2]
+ {\getbuffer[4a]} {\type {xstep=.10} and \type {shape="curve"}}
+ {\getbuffer[4b]} {\type {xstep=.01} and \type {shape="curve"}}
+ {\getbuffer[4c]} {\type {xstep=.10} and \type {shape="line"}}
+ {\getbuffer[4d]} {\type {xstep=.01} and \type {shape="line"}}
+ \stopcombination
+\stopplacefigure
+
+You can manipulate the axis (a bit) by tweaking the first and last ticks. In the
+case of \in {figure} [function:5] we also put the shape on top of the axis.
+
+\startbuffer[5]
+\startMPcode{doublefun}
+ draw lmt_function [
+ xfirst = 9, xlast = 21, ylarge = 2, ysmall = 1/5,
+ yfirst = -1, ylast = 1, xlarge = 2, xsmall = 1/4,
+
+ xmin = 10, xmax = 20, xstep = .25,
+ ymin = -1, ymax = 1,
+
+ drawcolor = "darkmagenta",
+ shape = "steps",
+ code = "0.5 * math.random(-2,2)",
+ linewidth = .025mm,
+ offset = .1mm,
+ reverse = true,
+ ]
+ xsized TextWidth
+ ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[5][option=TEX]
+
+\startplacefigure[reference=function:5]
+ \getbuffer[5]
+\stopplacefigure
+
+The whole repertoire of parameters (in case of doubt just check the source code as this
+kind of code is not that hard to follow) is:
+
+\starttabulate[|T|T|T|p|]
+\FL
+\BC name \BC type \BC default \BC comment \NC \NR
+\ML
+\NC sx \NC numeric \NC 1mm \NC horizontal scale factor \NC \NR
+\NC sy \NC numeric \NC 1mm \NC vertical scale factor \NC \NR
+\NC offset \NC numeric \NC 0 \NC \NC \NR
+\NC xmin \NC numeric \NC 1 \NC \NC \NR
+\NC xmax \NC numeric \NC 1 \NC \NC \NR
+\NC xstep \NC numeric \NC 1 \NC \NC \NR
+\NC xsmall \NC numeric \NC \NC optional step of small ticks \NC \NR
+\NC xlarge \NC numeric \NC \NC optional step of large ticks \NC \NR
+\NC xlabels \NC string \NC no \NC \type {yes}, \type {no} or \type {nolimits} \NC \NR
+\NC xticks \NC string \NC bottom \NC possible locations are \type {top}, \type {middle} and \type {bottom} \NC \NR
+\NC xcaption \NC string \NC \NC \NC \NR
+\NC ymin \NC numeric \NC 1 \NC \NC \NR
+\NC ymax \NC numeric \NC 1 \NC \NC \NR
+\NC ystep \NC numeric \NC 1 \NC \NC \NR
+\NC ysmall \NC numeric \NC \NC optional step of small ticks \NC \NR
+\NC ylarge \NC numeric \NC \NC optional step of large ticks \NC \NR
+\NC xfirst \NC numeric \NC \NC left of \type {xmin} \NC \NR
+\NC xlast \NC numeric \NC \NC right of \type {xmax} \NC \NR
+\NC yfirst \NC numeric \NC \NC below \type {ymin} \NC \NR
+\NC ylast \NC numeric \NC \NC above \type {ymax} \NC \NR
+\NC ylabels \NC string \NC no \NC \type {yes}, \type {no} or \type {nolimits} \NC \NR
+\NC yticks \NC string \NC left \NC possible locations are \type {left}, \type {middle} and \type {right} \NC \NR
+\NC ycaption \NC string \NC \NC \NC \NR
+\NC code \NC string \NC \NC \NC \NR
+\NC close \NC boolean \NC false \NC \NC \NR
+\NC shape \NC string \NC curve \NC or \type {line} \NC \NR
+\NC fillcolor \NC string \NC \NC \NC \NR
+\NC drawsize \NC numeric \NC 1 \NC \NC \NR
+\NC drawcolor \NC string \NC \NC \NC \NR
+\NC frame \NC string \NC \NC options are \type {yes}, \type {ticks} and \type {sticks} \NC \NR
+\NC linewidth \NC numeric \NC .05mm \NC \NC \NR
+\NC pointsymbol \NC string \NC \NC like type {dots} \NC \NR
+\NC pointsize \NC numeric \NC 2 \NC \NC \NR
+\NC pointcolor \NC string \NC \NC \NC \NR
+\NC xarrow \NC string \NC \NC \NC \NR
+\NC yarrow \NC string \NC \NC \NC \NR
+\NC reverse \NC boolean \NC false \NC when \type {true} draw the function between axis and labels \NC \NR
+\LL
+\stoptabulate
+
+\stopchapter
+
+\stopcomponent
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-grid.tex b/doc/context/sources/general/manuals/luametafun/luametafun-grid.tex
new file mode 100644
index 000000000..719d8023d
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-grid.tex
@@ -0,0 +1,11 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-grid
+
+\startchapter[title={Grid}]
+
+\stopchapter
+
+\stopcomponent
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-interface.tex b/doc/context/sources/general/manuals/luametafun/luametafun-interface.tex
new file mode 100644
index 000000000..662ae61a8
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-interface.tex
@@ -0,0 +1,155 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-interface
+
+\startchapter[title={Interface}]
+
+Because graphic solutions are always kind of personal or domain driven it makes not
+much sense to cook up very generic solutions. If you have a project where \METAPOST\
+can be of help, it also makes sense to spend some time on implementing the basics that
+you need. In that case you can just copy and tweak what is there. The easiest way to
+do that is to make a test file and use:
+
+\starttyping[option=TEX]
+\startMPpage
+ % your code
+\stopMPpage
+\stoptyping
+
+Often you don't need to write macros, and standard drawing commands will do the
+job, but when you find yourself repeating code, a wapper might make sense. And
+this is why we have this key|/|value interface: it's easier to abstract your
+settings than to pass them as (expression or text) arguments to a macro,
+especially when there are many.
+
+You can find many examples of the key|/|value driven user interface in the source
+files and these are actually not that hard to understand when you know a bit of
+\METAPOST\ and the additional macros that come with \METAFUN. In case you wonder
+about overhead: the performance of this mechanism is pretty good.
+
+Although the parameter handler runs on top of the \LUA\ interface, you don't need
+to use \LUA\ unless you find that \METAPOST\ can't do the job. I won't give
+examples of coding because I think that the source of \METAFUN\ provides enough
+clues, especially the file \type {mp-lmtx.mpxl}. As the name suggests this is
+part of the \CONTEXT\ version \LMTX, which runs on top of \LUAMETATEX. I leave it
+open if I will backport this functionality to \LUATEX\ and therefore \MKIV.
+
+An excellent explanation of this interface can be found at:
+
+\starttyping
+https://adityam.github.io/context-blog/post/new-metafun-interface/
+\stoptyping
+
+So (at least for now) here I can stick to just mentioning the currently stable
+interface macros:
+
+\starttabulate[|T|l|pl|]
+\FL
+\NC presetparameters \NC \type {name [...]} \NC
+ Assign default values to a category of parameters. Sometimes it makes sense
+ not to set a default, because then you can check if a parameter has been set
+ at all.
+ \NC \NR
+\NC applyparameters \NC \type {name macro} \NC
+ This prepares the parameter handler for the given category and calls the
+ given macro when that is done.
+ \NC \NR
+\NC getparameters \NC \type {name [...]} \NC
+ The parameters given after the category name are set.
+ \NC \NR
+\ML
+\NC hasparameter \NC \type {names} \NC
+ Returns \type {true} when a parameter is set, and \type {false} otherwise.
+ \NC \NR
+\NC hasoption \NC \type {names options} \NC
+ Returns \type {true} when there is overlap in given options, and \type
+ {false} otherwise.
+ \NC \NR
+\ML
+\NC getparameter \NC \type {names} \NC
+ Resolves the parameter with the given name. because a parameter itself can
+ have a parameter list you can pass additional names to reach the final
+ destination.
+ \NC \NR
+\NC getparameterdefault \NC \type {names} \NC
+ Resolves the parameter with the given name. because a parameter itself can
+ have a parameter list you can pass additional names to reach the final
+ destination. The last value is used when no parameter is found.
+ \NC \NR
+\ML
+\NC getparametercount \NC \type {names} \NC
+ Returns the size if a list (array).
+ \NC \NR
+\NC getmaxparametercount \NC \type {names} \NC
+ Returns the size if a list (array) but descends into lists to find the largest size
+ of a sublist.
+ \NC \NR
+\ML
+\NC getparameterpath \NC \type {names string boolean} \NC
+ Returns the parameter as path. The optional string is one of \type {--},
+ \type {..} or \type {...} and the also optional boolean will force a closed
+ path.
+ \NC \NR
+\NC getparameterpen \NC \type {names} \NC
+ Returns the parameter as pen (path).
+ \NC \NR
+\NC getparametertext \NC \type {names boolean} \NC
+ Returns the parameter as string. The boolean can be used to force prepending
+ a so called \type {\strut}.
+ \NC \NR
+\ML
+\NC pushparameters \NC \type {category} \NC
+ Pushed the given (sub) category onto the stack so that we don't need to give
+ the category each time.
+ \NC \NR
+\NC popparameters \NC \NC
+ Pops the current (sub) category from the stack.
+ \NC \NR
+\LL
+\stoptabulate
+
+Most commands accept a list of strings separated by one or more spaces, The
+resolved will then stepwise descend into the parameter tree. This means that a
+parameter itself can refer to a list. When a value is an array and the last name
+is a number, the value at the given index will be returned.
+
+\starttyping
+"category" "name" ... "name"
+"category" "name" ... number
+\stoptyping
+
+The \type {category} is not used when we have pushed a (sub) category which can
+save you some typing and also is more efficient. Of course than can mean that you
+need to store values at a higher level when you need them at a deeper level.
+
+There are quite some extra helpers that relate to this mechanism, at the
+\METAPOST\ end as well as at the \LUA\ end. They aim for instance at efficiently
+dealing with paths and can be seen at work in the mentioned module.
+
+There is one thing you should notice. While \METAPOST\ has numeric, string,
+boolean and path variables that can be conveniently be passed to and from \LUA,
+communicating colors is a bit of a hassle. This is because \RGB\ and \CMYK\
+colors and gray scales use different types. For this reason it is strongly
+recommended to use strings that refer to predefined colors instead. This also
+enforces consistency with the \TEX\ end. As convenience you can define colors at
+the \METAFUN\ end.
+
+\startbuffer
+\startMPcode
+ definecolor [ name = "MyColor", r = .5, g = .25, b = .25 ]
+
+ fill fullsquare xyscaled (TextWidth,5mm) withcolor "MyColor" ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+\stopchapter
+
+\stopcomponent
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-introduction.tex b/doc/context/sources/general/manuals/luametafun/luametafun-introduction.tex
new file mode 100644
index 000000000..6892b21ce
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-introduction.tex
@@ -0,0 +1,88 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-introduction
+
+\startchapter[title={Introduction}]
+
+For quite a while, around since 1996, the integration of \METAPOST\ into
+\CONTEXT\ became sort of mature but, it took decades of stepwise refinement to
+reach the state that we're in now. In this manual I will discuss some of the
+features that became possible by combining \LUA\ and \METAPOST. We already had
+quite a bit of that for a decade but in 2018, when \LUAMETATEX\ showed up a next
+stage was started.
+
+Before we go into details it is good to summarize the steps that were involved in
+integrating \METAPOST\ and \TEX\ in \CONTEXT. It indicates a bit what we had and
+have to deal with which in turn lead to the interfaces we now have.
+
+Originally, \TEX\ had no graphic capabilities: it just needed to know dimensions
+of the graphics and pass some basic information about what to include to the
+\DVI\ post processor. So, a \METAPOST\ graphic was normally processed outside the
+current run, resulting in \POSTSCRIPT\ graphic, that then had to be included. In
+\PDFTEX\ there were some more built in options, and therefore the \METAPOST\ code
+could be processed runtime using some (generic) \TEX\ macros that I wrote.
+However, that engine still had to launch \METAPOST\ for each graphic, although we
+could accumulate them and do that between runs. Immediate processing means that
+we immediately know the dimensions, while a collective run is faster. In \LUATEX\
+this all changed to very fast runtime processing, made possible because the
+\METAPOST\ library is embedded in the engine, a decision that we made early in
+the project and never regret.
+
+With \PDFTEX\ the process was managed by the \type {texexec} \CONTEXT\ runner but
+with \LUATEX\ it stayed under the control of the current run. In the case of
+\PDFTEX\ the actual embedding was done by \TEX\ macros that interpreted the
+(relatively simple) \POSTSCRIPT\ code and turned it into \PDF\ literals. In
+\LUATEX\ that job was delegated to \LUA.
+
+When using \PDFTEX\ with independent \METAPOST\ runs support for special color
+spaces, transparency, embedded graphics, outline text, shading and more was
+implemented using specials and special colors where the color served as reference
+to some special extension. This works quite well. In \LUATEX\ the pre- and
+postscript features, which are properties of picture objects, are used.
+
+In all cases, some information about the current run, for instance layout related
+information, or color information, has to be passed to the rather isolated
+\METAPOST\ run. In the case if \LUATEX\ (and \MKIV) the advantage is that
+processing optional text happens in the same process so there we don't need to
+pass information about for instance the current font setup.
+
+In \LUATEX\ the \METAPOST\ library has a \type {runscript} feature, which will
+call \LUA\ with the given code. This permitted a better integration: we could now
+ask for specific information (to the \TEX\ end) instead of passing it from the
+\TEX\ end with each run. In \LUAMETATEX\ another feature was added: access to the
+scanners from the \LUA\ end. Although we could already fetch some variables when
+in \LUA\ this made it possible to extend the \METAPOST\ language in ways not
+possible before.
+
+Already for a while Alan Braslau and I were working on some new \METAFUN\ code
+that exploits all these new features. When the scanners came available I sat down
+and started working on new interfaces and in this manual I will discuss some of
+these. Some of them are illustrative, others are probably rather useful. The core
+of what we could call \LUAMETAFUN\ (or \METAFUN\ XL when we use the file
+extension as indicator) is a key|-|value interface as we have at the \TEX\ end.
+This interface relates to \CONTEXT\ \LMTX\ development and therefore related
+files have a different suffix: \type {mpxl}. However, keep in mind that some are
+just wrappers around regular \METAPOST\ code so you have the full power of
+traditional \METAPOST\ at hand.
+
+We can never satisfy all needs, so to some extent this manual also demonstrates
+how to roll out your own code, but for that you also need to peek into the
+\METAFUN\ source code too. It will take a while for this manual to complete. I
+also expect other users to come up with solutions, so maybe in the end we will
+have a collection of modules for specific tasks.
+
+\startlines
+Hans Hagen
+Hasselt NL
+August 2019 (and beyond)
+\stoplines
+
+\stopchapter
+
+\stopcomponent
+
+% I started writing this in 2019, a few days after seeing Wende live in Zeist (
+% (YT: WENDE - # MENS), one of the best shows of that year, a clear reminder of
+% timeless versatility.
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-mesh-examples.tex b/doc/context/sources/general/manuals/luametafun/luametafun-mesh-examples.tex
new file mode 100644
index 000000000..87f6d105f
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-mesh-examples.tex
@@ -0,0 +1,161 @@
+\startbuffer[1]
+\startuseMPgraphic{MyPath1}
+ fill OverlayBox withcolor "darkyellow" ;
+ save p ; path p[] ;
+ p1 := unitsquare xysized( OverlayWidth/4, OverlayHeight/4) ;
+ p2 := unitsquare xysized(2OverlayWidth/4,3OverlayHeight/5) shifted ( OverlayWidth/4,0) ;
+ p3 := unitsquare xysized( OverlayWidth/4, OverlayHeight ) shifted (3OverlayWidth/4,0) ;
+ fill p1 withcolor "darkred" ;
+ fill p2 withcolor "darkblue" ;
+ fill p3 withcolor "darkgreen" ;
+ draw lmt_mesh [ paths = { p1, p2, p3 } ] ;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+\stopbuffer
+
+\startbuffer[2]
+\startuseMPgraphic{MyPath2}
+ save q ; path q ; q := unitcircle xysized(OverlayWidth,OverlayHeight) ;
+ save p ; path p ; p := for i=1 upto length(q) :
+ (center q) -- (point (i-1) of q) -- (point i of q) -- (center q) --
+ endfor cycle ;
+ fill q withcolor "darkgray" ;
+ draw lmt_mesh [
+ trace = true,
+ paths = { p }
+ ] withcolor "darkred" ;
+
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+\stopbuffer
+
+\startbuffer[3]
+\startuseMPgraphic{MyPath3}
+ save q ; path q ; q := unitcircle xysized(OverlayWidth,OverlayHeight) randomized 3mm ;
+ fill q withcolor "darkgray" ;
+ draw lmt_mesh [
+ trace = true,
+ paths = { meshed(q,OverlayBox,.05) }
+ ] withcolor "darkgreen" ;
+ % draw OverlayMesh(q,.025) withcolor "darkgreen" ;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+\stopbuffer
+
+\startbuffer[4]
+\startuseMPgraphic{MyPath4}
+ save q ; path q ; q := unitcircle xysized(OverlayWidth,OverlayHeight) randomized 3mm ;
+ fill q withcolor "darkgray" ;
+ draw lmt_mesh [
+ trace = true,
+ auto = true,
+ step = 0.0125,
+ paths = { q }
+ ] withcolor "darkyellow" ;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+\stopbuffer
+
+\startbuffer[5]
+\startuseMPgraphic{MyPath5}
+ save q ; path q ; q := unitdiamond xysized(OverlayWidth,OverlayHeight) randomized 2mm ;
+ q := q shifted - center q shifted center OverlayBox ;
+ fill q withcolor "darkgray" ;
+ draw lmt_mesh [
+ trace = true,
+ auto = true,
+ step = 0.0125,
+ paths = { q }
+ ] withcolor "darkmagenta" ;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+\stopbuffer
+
+\startbuffer[6]
+\startuseMPgraphic{MyPath6}
+ save p ; path p[] ;
+ p1 := p2 := fullcircle xysized(2OverlayWidth/5,2OverlayHeight/3) ;
+ p1 := p1 shifted - center p1 shifted center OverlayBox shifted (-1OverlayWidth/4,0) ;
+ p2 := p2 shifted - center p2 shifted center OverlayBox shifted ( 1OverlayWidth/4,0) ;
+ fill p1 withcolor "middlegray" ;
+ fill p2 withcolor "middlegray" ;
+ draw lmt_mesh [
+ trace = true,
+ auto = true,
+ step = 0.02,
+ paths = { p1, p2 }
+ ] withcolor "darkcyan" ;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+\stopbuffer
+
+\startbuffer[7]
+\startuseMPgraphic{MyPath7}
+ save p ; path p[] ;
+ p1 := p2 := fullcircle xysized(2OverlayWidth/5,2OverlayHeight/3) rotated 45 ;
+ p1 := p1 shifted - center p1 shifted center OverlayBox shifted (-1OverlayWidth/4,0) ;
+ p2 := p2 shifted - center p2 shifted center OverlayBox shifted ( 1OverlayWidth/4,0) ;
+ fill p1 withcolor "middlegray" ;
+ fill p2 withcolor "middlegray" ;
+ draw lmt_mesh [
+ trace = true,
+ auto = true,
+ step = 0.01,
+ box = OverlayBox enlarged -5mm,
+ paths = { p1, p2 }
+ ] withcolor "darkcyan" ;
+ draw OverlayBox enlarged -5mm withcolor "darkgray" ;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+\stopbuffer
+
+\continueifinputfile {luametafun-mesh-examples.tex}
+
+\setupbodyfont[dejavu]
+
+\setupinteraction
+ [state=start,
+ color=white,
+ contrastcolor=white]
+
+\starttext
+
+ \getbuffer[1,2,3,4,5,6,7]
+
+ \defineoverlay[MyPath1][\useMPgraphic{MyPath1}]
+ \defineoverlay[MyPath2][\useMPgraphic{MyPath2}]
+ \defineoverlay[MyPath3][\useMPgraphic{MyPath3}]
+ \defineoverlay[MyPath4][\useMPgraphic{MyPath4}]
+ \defineoverlay[MyPath5][\useMPgraphic{MyPath5}]
+ \defineoverlay[MyPath6][\useMPgraphic{MyPath6}]
+ \defineoverlay[MyPath7][\useMPgraphic{MyPath7}]
+
+ \startTEXpage
+ \button[height=3cm,width=4cm,background=MyPath1,frame=off]{Example 1}[realpage(2)]
+ \stopTEXpage
+
+ \startTEXpage
+ \button[height=3cm,width=4cm,background=MyPath2,frame=off]{Example 2}[realpage(3)]
+ \stopTEXpage
+
+ \startTEXpage
+ \button[height=3cm,width=4cm,background=MyPath3,frame=off]{Example 3}[realpage(4)]
+ \stopTEXpage
+
+ \startTEXpage
+ \button[height=3cm,width=4cm,background=MyPath4,frame=off]{Example 4}[realpage(5)]
+ \stopTEXpage
+
+ \startTEXpage
+ \button[height=3cm,width=4cm,background=MyPath5,frame=off]{Example 5}[realpage(6)]
+ \stopTEXpage
+
+ \startTEXpage
+ \button[height=3cm,width=4cm,background=MyPath6,frame=off]{Example 6}[realpage(7)]
+ \stopTEXpage
+
+ \startTEXpage
+ \button[height=3cm,width=4cm,background=MyPath7,frame=off]{Example 7}[realpage(1)]
+ \stopTEXpage
+
+\stoptext
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-mesh.tex b/doc/context/sources/general/manuals/luametafun/luametafun-mesh.tex
new file mode 100644
index 000000000..2fdb31250
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-mesh.tex
@@ -0,0 +1,78 @@
+% language=us
+
+\environment luametafun-style
+\environment luametafun-mesh-examples
+
+\startcomponent luametafun-mesh
+
+\startchapter[title={Mesh}]
+
+This is more a gimmick than of real practical use. A mesh is a set of paths that
+gets transformed into hyperlinks. So, as a start you need to enable these:
+
+\starttyping[option=TEX]
+\setupinteraction
+ [state=start,
+ color=white,
+ contrastcolor=white]
+\stoptyping
+
+We just give a bunch of examples of meshes. A path is divided in smaller paths and
+each of them is part of the same hyperlink. An application is for instance clickable
+maps but (so far) only Acrobat supports such paths.
+
+\typebuffer[1][option=TEX]
+
+Such a definition is used as follows. First we define the mesh as overlay:
+
+\starttyping[option=TEX]
+\defineoverlay[MyPath1][\useMPgraphic{MyPath1}]
+\stoptyping
+
+Then, later on, this overlay can be used as background for a button. Here we just
+jump to another page. The rendering is shown in \in {figure} [mesh:1].
+
+\starttyping[option=TEX]
+\button
+ [height=3cm,
+ width=4cm,
+ background=MyPath1,
+ frame=off]
+ {Example 1}
+ [realpage(2)]
+\stoptyping
+
+\startplacefigure[reference=mesh:1]
+ \externalfigure[luametafun-mesh-examples][page=1,width=.45\textwidth]
+\stopplacefigure
+
+More interesting are non|-|rectangular shapes so we show a bunch of them. You can
+pass multiple paths, influence the accuracy by setting the number of steps and show
+the mesh with the tracing option.
+
+\typebuffer[2][option=TEX]
+\typebuffer[3][option=TEX]
+\typebuffer[4][option=TEX]
+\typebuffer[5][option=TEX]
+\typebuffer[6][option=TEX]
+\typebuffer[7][option=TEX]
+
+This is typical a feature that, if used at all, needs some experimenting but at
+least the traced images look interesting enough. The six examples are shown in
+\in {figure} [mesh:2].
+
+\startplacefigure[reference=mesh:2]
+ \startcombination[2*3]
+ {\externalfigure[luametafun-mesh-examples][page=2,width=.45\textwidth]} {\type {MyPath2}}
+ {\externalfigure[luametafun-mesh-examples][page=3,width=.45\textwidth]} {\type {MyPath3}}
+ {\externalfigure[luametafun-mesh-examples][page=4,width=.45\textwidth]} {\type {MyPath4}}
+ {\externalfigure[luametafun-mesh-examples][page=5,width=.45\textwidth]} {\type {MyPath5}}
+ {\externalfigure[luametafun-mesh-examples][page=6,width=.45\textwidth]} {\type {MyPath6}}
+ {\externalfigure[luametafun-mesh-examples][page=7,width=.45\textwidth]} {\type {MyPath7}}
+ \stopcombination
+\stopplacefigure
+
+\stopchapter
+
+\stopcomponent
+
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-outline.tex b/doc/context/sources/general/manuals/luametafun/luametafun-outline.tex
new file mode 100644
index 000000000..e2cdb4226
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-outline.tex
@@ -0,0 +1,188 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-outline
+
+\startchapter[title={Outline}]
+
+In a regular text you can have outline characters by setting a (pseudo) font
+feature but sometimes you want to play a bit more with this. In \METAFUN\ we
+always had that option. In \MKII\ we call \type {pstoedit} to turn text into
+outlines, in \MKIV\ we do that by manipulating the shapes directly. And, as with
+some other extensions, in \LMTX\ a new interface has been added, but the
+underlying code is the same as in \MKIV.
+
+\startbuffer[1a]
+\startMPcode{doublefun}
+ draw lmt_outline [
+ text = "hello"
+ kind = "draw",
+ drawcolor = "darkblue",
+ ] xsized .45TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[1b]
+\startMPcode{doublefun}
+ draw lmt_outline [
+ text = "hello",
+ kind = "both",
+ fillcolor = "middlegray",
+ drawcolor = "darkgreen",
+ rulethickness = 1/5,
+ ] xsized .45TextWidth ;
+\stopMPcode
+\stopbuffer
+
+In \in {figure} [outline:1] we see two examples:
+
+\typebuffer[1a][option=TEX]
+
+and
+
+\typebuffer[1b][option=TEX]
+
+\startplacefigure[reference=outline:1,title={Drawing and|/|or filling an outline.}]
+ \startcombination
+ {\getbuffer[1a]} {\type {kind=draw}}
+ {\getbuffer[1b]} {\type {kind=both}}
+ \stopcombination
+\stopplacefigure
+
+Normally the fill ends up below the draw but we can reverse the order, as in
+\in {figure} [outline:2], where we coded the leftmost example as:
+
+\startbuffer[2a]
+\startMPcode{doublefun}
+ draw lmt_outline [
+ text = "hello",
+ kind = "reverse",
+ fillcolor = "darkred",
+ drawcolor = "darkblue",
+ rulethickness = 1/2,
+ ] xsized .45TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[2b]
+\startMPcode{doublefun}
+ draw lmt_outline [
+ text = "hello",
+ kind = "both",
+ fillcolor = "darkred",
+ drawcolor = "darkblue",
+ rulethickness = 1/2,
+ ] xsized .45TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[2a][option=TEX]
+
+\startplacefigure[reference=outline:2,title={Reversing the order of drawing and filling.}]
+ \startcombination
+ {\getbuffer[2a]} {\type {kind=reverse}}
+ {\getbuffer[2b]} {\type {kind=both}}
+ \stopcombination
+\stopplacefigure
+
+It is possible to fill and draw in one operation, in which case the same color is
+used for both, see \in {figure} [outline:3] for an example fo this. This is a low
+level optimization where the shape is only output once.
+
+\startbuffer[3a]
+\startMPcode{doublefun}
+ draw lmt_outline [
+ text = "hello",
+ kind = "fillup",
+ fillcolor = "darkgreen",
+ rulethickness = 1/5,
+ ] xsized .45TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[3b]
+\startMPcode{doublefun}
+ draw lmt_outline [
+ text = "hello",
+ kind = "fill",
+ fillcolor = "darkgreen",
+ rulethickness = 1/5,
+ ] xsized .45TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\startplacefigure[reference=outline:3,title={Combining a fill with a draw in the same color.}]
+ \startcombination
+ {\getbuffer[3a]} {\type {kind=fillup}}
+ {\getbuffer[3b]} {\type {kind=fill}}
+ \stopcombination
+\stopplacefigure
+
+
+This interface is much nicer than the one where each variant (the parameter \type
+{kind} above) had its own macro due to the need to group properties of the
+outline and fill. Let's show some more:
+
+\startbuffer[4]
+\startMPcode{doublefun}
+ draw lmt_outline [
+ text = "\obeydiscretionaries\samplefile{tufte}",
+ align = "normal",
+ kind = "draw",
+ drawcolor = "darkblue",
+ ] xsized TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[4][option=TEX]
+
+In this case we feed the text into the \type {\framed} macro so that we get a
+properly aligned paragraph of text, as demonstrated in \in {figure} [outline:4]
+\in {and} [outline:5]. If you want more trickery you can of course use any
+\CONTEXT\ command (including \type {\framed} with all kind of options) in the
+text.
+
+\startplacefigure[reference=outline:4,title={Outlining a paragraph of text.}]
+ \getbuffer[4]
+\stopplacefigure
+
+\startbuffer[5]
+\startMPcode{doublefun}
+ draw lmt_outline [
+ text = "\obeydiscretionaries\samplefile{ward}",
+ align = "normal,tolerant",
+ style = "bold",
+ width = 10cm,
+ kind = "draw",
+ drawcolor = "darkblue",
+ ] xsized TextWidth ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[5][option=TEX]
+
+\startplacefigure[reference=outline:4,title={Outlining a paragraph of text with a specific width.}]
+ \getbuffer[5]
+\stopplacefigure
+
+We summarize the parameters:
+
+\starttabulate[|T|T|T|p|]
+\FL
+\BC name \BC type \BC default \BC comment \NC \NR
+\ML
+\NC text \NC string \NC \NC \NC \NR
+\NC kind \NC string \NC draw \NC One of \type {draw}, \type {fill}, \type {both}, \type {reverse} and \type {fillup}. \NC \NR
+\NC fillcolor \NC string \NC \NC \NC \NR
+\NC drawcolor \NC string \NC \NC \NC \NR
+\NC rulethickness \NC numeric \NC 1/10 \NC \NC \NR
+\NC align \NC string \NC \NC \NC \NR
+\NC style \NC string \NC \NC \NC \NR
+\NC width \NC numeric \NC \NC \NC \NR
+\LL
+\stoptabulate
+
+\stopchapter
+
+\stopcomponent
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-placeholder.tex b/doc/context/sources/general/manuals/luametafun/luametafun-placeholder.tex
new file mode 100644
index 000000000..3627bbfd0
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-placeholder.tex
@@ -0,0 +1,163 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-placeholder
+
+\startchapter[title={Placeholder}]
+
+Placeholders are an old \CONTEXT\ features and have been around since we started using
+\METAPOST. They are used as dummy figure, just in case one is not (yet) present. They
+are normally activated by loading a \METAFUN\ library:
+
+\starttyping[option=TEX]
+\useMPLibrary[dum]
+\stoptyping
+
+Just because it could be done conveniently, placeholders are now defined at the
+\METAPOST\ end instead of as useable \METAPOST\ graphic at the \TEX\ end. The
+variants and options are demonstrated using side floats.
+
+\startbuffer[1]
+\startMPcode
+ lmt_placeholder [
+ width = 4cm,
+ height = 3cm,
+ color = "red",
+ alternative = "circle".
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\startplacefigure[location=left]
+ \getbuffer[1]
+\stopplacefigure
+
+\typebuffer[1][option=TEX]
+
+In addition to the traditional random circle we now also provide rectangles
+and triangles. Maybe some day more variants will show up.
+
+\startbuffer[2]
+\startMPcode
+ lmt_placeholder [
+ width = 4cm,
+ height = 3cm,
+ color = "green",
+ alternative = "square".
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\startplacefigure[location=left]
+ \getbuffer[2]
+\stopplacefigure
+
+\typebuffer[2][option=TEX]
+
+Here we set the colors but in the image placeholder mechanism we cycle through
+colors automatically. Here we use primary, rather dark, colors.
+
+\startbuffer[3]
+\startMPcode
+ lmt_placeholder [
+ width = 4cm,
+ height = 3cm,
+ color = "blue",
+ alternative = "triangle".
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\startplacefigure[location=left]
+ \getbuffer[3]
+\stopplacefigure
+
+\typebuffer[3][option=TEX]
+
+\startbuffer[4a]
+\startMPcode
+ lmt_placeholder [
+ width = 4cm,
+ height = 3cm,
+ color = "yellow",
+ alternative = "circle".
+ reduction = 0,
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[4b]
+\startMPcode
+ lmt_placeholder [
+ width = 4cm,
+ height = 3cm,
+ color = "yellow",
+ alternative = "circle".
+ reduction = 0.25,
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[4c]
+\startMPcode
+ lmt_placeholder [
+ width = 4cm,
+ height = 3cm,
+ color = "yellow",
+ alternative = "circle".
+ reduction = 0.50,
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\startbuffer[4d]
+\startMPcode
+ lmt_placeholder [
+ width = 4cm,
+ height = 3cm,
+ color = "yellow",
+ alternative = "circle".
+ reduction = 0.75,
+ ] ;
+\stopMPcode
+\stopbuffer
+
+If you want less dark colors, the \type {reduction} parameter can be used to
+interpolate between the given color and white; its value is therefore a value
+between zero (default) and 1 (rather pointless as it produces white).
+
+\startplacefigure[location=left]
+ \startcombination[2*2]
+ {\getbuffer[4a]} {0}
+ {\getbuffer[4b]} {0.25}
+ {\getbuffer[4c]} {0.50}
+ {\getbuffer[4d]} {0.75}
+ \stopcombination
+\stopplacefigure
+
+We demonstrate this with four variants, all circles. Of course you can also use
+lighter colors, but this option was needed for the image placeholders anyway.
+
+\typebuffer[4b][option=TEX]
+
+\flushsidefloats
+
+There are only a few possible parameters. As you can see, proper dimensions need
+to be given because the defaults are pretty small.
+
+\starttabulate[|T|T|T|p|]
+\FL
+\BC name \BC type \BC default \BC comment \NC \NR
+\ML
+\NC color \NC string \NC red \NC \NC \NR
+\NC width \NC numeric \NC 1 \NC \NC \NR
+\NC height \NC numeric \NC 1 \NC \NC \NR
+\NC reduction \NC numeric \NC 0 \NC \NC \NR
+\NC alternative \NC string \NC circle \NC \NC \NR
+\LL
+\stoptabulate
+
+\stopchapter
+
+\stopcomponent
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-shade.tex b/doc/context/sources/general/manuals/luametafun/luametafun-shade.tex
new file mode 100644
index 000000000..a139be954
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-shade.tex
@@ -0,0 +1,230 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-shade
+
+\startchapter[title={Shade}]
+
+{\em This interface is still experimental!}
+
+Shading is complex. We go from one color to another on a continuum either linear
+or circular. We have to make sure that we cover the whole shape and that means
+that we have to guess a little, although one can influence this with parameters.
+It can involve a bit of trial and error, which is more complex that using a
+graphical user interface but this is the price we pay. It goes like this:
+
+\startbuffer[1]
+\startMPcode
+definecolor [ name = "MyColor3", r = 0.22, g = 0.44, b = 0.66 ] ;
+definecolor [ name = "MyColor4", r = 0.66, g = 0.44, b = 0.22 ] ;
+
+draw lmt_shade [
+ path = fullcircle scaled 4cm,
+ direction = "right",
+ domain = { 0, 2 },
+ colors = { "MyColor3", "MyColor4" },
+] ;
+
+draw lmt_shade [
+ path = fullcircle scaled 3cm,
+ direction = "left",
+ domain = { 0, 2 },
+ colors = { "MyColor3", "MyColor4" },
+] shifted (45mm,0) ;
+
+draw lmt_shade [
+ path = fullcircle scaled 5cm,
+ direction = "up",
+ domain = { 0, 2 },
+ colors = { "MyColor3", "MyColor4" },
+] shifted (95mm,0) ;
+
+draw lmt_shade [
+ path = fullcircle scaled 1cm,
+ direction = "down",
+ domain = { 0, 2 },
+ colors = { "MyColor3", "MyColor4" },
+] shifted (135mm,0) ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[1][option=TEX]
+
+Normally this is good enough as demonstrated in \in {figure} [shade:1] because
+we use shades as backgrounds. In the case of a circular shade we need to tweak
+the domain because guessing doesn't work well.
+
+\startplacefigure[reference=shade:1,title={Simple circular shades.}]
+ \getbuffer[1]
+\stopplacefigure
+
+\startbuffer[2]
+\startMPcode
+draw lmt_shade [
+ path = fullsquare scaled 4cm,
+ alternative = "linear",
+ direction = "right",
+ colors = { "MyColor3", "MyColor4" },
+] ;
+
+draw lmt_shade [
+ path = fullsquare scaled 3cm,
+ direction = "left",
+ alternative = "linear",
+ colors = { "MyColor3", "MyColor4" },
+] shifted (45mm,0) ;
+
+draw lmt_shade [
+ path = fullsquare scaled 5cm,
+ direction = "up",
+ alternative = "linear",
+ colors = { "MyColor3", "MyColor4" },
+] shifted (95mm,0) ;
+
+draw lmt_shade [
+ path = fullsquare scaled 1cm,
+ direction = "down",
+ alternative = "linear",
+ colors = { "MyColor3", "MyColor4" },
+] shifted (135mm,0) ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[2][option=TEX]
+
+\startplacefigure[reference=shade:2,title={Simple rectangular shades.}]
+ \getbuffer[2]
+\stopplacefigure
+
+The \type {direction} relates to the boundingbox. Instead of a keyword you can
+also give two values, indicating points on the boundingbox. Because a boundingbox
+has four points, the \type {up} direction is equivalent to \type {{0.5,2.5}}.
+
+The parameters \type {center}, \type {factor}, \type {vector} and \type {domain}
+are a bit confusing but at some point the way they were implemented made sense,
+so we keep them as they are. The center moves the center of the path that is used
+as anchor for one color proportionally to the bounding box: the given factor is
+multiplied by half the width and height.
+
+\startbuffer[3]
+\startMPcode
+draw lmt_shade [
+ path = fullcircle scaled 5cm,
+ domain = { .2, 1.6 },
+ center = { 1/10, 1/10 },
+ direction = "right",
+ colors = { "MyColor3", "MyColor4" },
+ trace = true,
+] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[3][option=TEX]
+
+\startplacefigure[reference=shade:3,title={Moving the centers.}]
+ \getbuffer[3]
+\stopplacefigure
+
+A vector takes the given points on the path as centers for the colors, see \in
+{figure} [shade:4].
+
+\startbuffer[4]
+\startMPcode
+draw lmt_shade [
+ path = fullcircle scaled 5cm,
+ domain = { .2, 1.6 },
+ vector = { 2, 4 },
+ direction = "right",
+ colors = { "MyColor3", "MyColor4" },
+ trace = true,
+] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[4][option=TEX]
+
+\startplacefigure[reference=shade:4,title={Using a vector (points).}]
+ \getbuffer[4]
+\stopplacefigure
+
+Messing with the radius in combination with the previously mentioned domain
+is really trial and error, as seen in \in {figure} [shade:5].
+
+\startbuffer[5]
+\startMPcode
+draw lmt_shade [
+ path = fullcircle scaled 5cm,
+ domain = { 0.5, 2.5 },
+ radius = { 2cm, 6cm },
+ direction = "right",
+ colors = { "MyColor3", "MyColor4" },
+ trace = true,
+] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[5][option=TEX]
+
+\startplacefigure[reference=shade:5,title={Tweaking the radius.}]
+ \getbuffer[5]
+\stopplacefigure
+
+But actually the radius used alone works quite well as shown in \in {figure}
+[shade:6].
+
+\startbuffer[6]
+\startMPcode
+draw lmt_shade [
+ path = fullcircle scaled 5cm,
+ colors = { "red", "green" },
+ trace = true,
+] ;
+
+draw lmt_shade [
+ path = fullcircle scaled 5cm,
+ colors = { "red", "green" },
+ radius = 2.5cm,
+ trace = true,
+] shifted (6cm,0) ;
+
+draw lmt_shade [
+ path = fullcircle scaled 5cm,
+ colors = { "red", "green" },
+ radius = 2.0cm,
+ trace = true,
+] shifted (12cm,0) ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[6][option=TEX]
+
+\startplacefigure[reference=shade:6,title={Just using the radius.}]
+ \getbuffer[6]
+\stopplacefigure
+
+\starttabulate[|T|T|T|p|]
+\FL
+\BC name \BC type \BC default \BC comment \NC \NR
+\ML
+\NC alternative \NC string \NC circular \NC or \type {linear} \NC \NR
+\NC path \NC path \NC \NC \NC \NR
+\NC trace \NC boolean \NC false \NC \NC \NR
+\NC domain \NC set of numerics \NC \NC \NC \NR
+\NC radius \NC numeric \NC \NC \NC \NR
+\NC \NC set of numerics \NC \NC \NC \NR
+\NC factor \NC numeric \NC \NC \NC \NR
+\NC origin \NC pair \NC \NC \NC \NR
+\NC \NC set of pairs \NC \NC \NC \NR
+\NC vector \NC set of numerics \NC \NC \NC \NR
+\NC colors \NC set of strings \NC \NC \NC \NR
+\NC center \NC numeric \NC \NC \NC \NR
+\NC \NC set of numerics \NC \NC \NC \NR
+\NC direction \NC string \NC \NC \type{up}, \type {down}, \type {left}, \type {right} \NC \NR
+\NC \NC set of numerics \NC \NC two points on the boundingbox \NC \NR
+\LL
+\stoptabulate
+
+\stopchapter
+
+\stopcomponent
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-style.tex b/doc/context/sources/general/manuals/luametafun/luametafun-style.tex
new file mode 100644
index 000000000..df469d0c0
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-style.tex
@@ -0,0 +1,55 @@
+\startenvironment luametafun-style
+
+\usemodule[abbreviations-smallcaps]
+
+\usemodule[scite]
+
+\setupbodyfont
+ [ibmplex,rm,10pt]
+
+\setupwhitespace
+ [big]
+
+\setuplayout
+ [topspace=10mm,
+ bottomspace=1cm,
+ backspace=2cm,
+ footerdistance=10mm,
+ footer=1cm,
+ % headerdistance=10mm,
+ % header=1cm,
+ topspace=20mm,
+ headerdistance=0mm,
+ header=0cm,
+ height=middle,
+ width=middle]
+
+\setupalign
+ [verytolerant]
+
+\setupheadertexts
+ []
+
+\setupfootertexts
+ [chapter][pagenumber]
+
+\setuphead
+ [chapter]
+ [color=darkgray,
+ style=\bfd]
+
+\setuphead
+ [section]
+ [color=darkgray,
+ style=\bfc]
+
+\setupfooter
+ [color=darkgray,
+ style=\bf]
+
+\setuplist
+ [chapter]
+ [before=,
+ after=]
+
+\stopenvironment
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-svg.tex b/doc/context/sources/general/manuals/luametafun/luametafun-svg.tex
new file mode 100644
index 000000000..f7aba06fd
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-svg.tex
@@ -0,0 +1,75 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-svg
+
+\startchapter[title={SVG}]
+
+There is not that much to tell about this command. It translates an \SVG\ image
+to \METAPOST\ operators. We took a few images from a mozilla emoji font:
+
+\startbuffer[2]
+\startMPcode
+ draw lmt_svg [
+ filename = "mozilla-svg-002.svg",
+ height = 2cm,
+ width = 8cm,
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[2][option=TEX]
+
+\startlinecorrection
+ \getbuffer[2]
+\stoplinecorrection
+
+Because we get pictures, you can do mess around with them:
+
+\startbuffer[1]
+\startMPcode
+ picture p ; p := lmt_svg [ filename = "mozilla-svg-001.svg" ] ;
+ numeric w ; w := bbwidth(p) ;
+ draw p ;
+ draw p xscaled -1 shifted (2.5*w,0);
+ draw p rotatedaround(center p,45) shifted (3.0*w,0) ;
+ draw image (
+ for i within p : if filled i :
+ draw pathpart i withcolor green ;
+ fi endfor ;
+ ) shifted (4.5*w,0);
+ draw image (
+ for i within p : if filled i :
+ fill pathpart i withcolor red withtransparency (1,.25) ;
+ fi endfor ;
+ ) shifted (6*w,0);
+\stopMPcode
+\stopbuffer
+
+\typebuffer[1][option=TEX]
+
+\startlinecorrection
+ \getbuffer[1]
+\stoplinecorrection
+
+Of course. often you won't know in advance what is inside the image and how (well)
+it has been defined so the previous example is more about showing some \METAPOST\
+muscle.
+
+The supported parameters are:
+
+\starttabulate[|T|T|T|p|]
+\FL
+\BC name \BC type \BC default \BC comment \NC \NR
+\ML
+\NC filename \NC path \NC \NC \NC \NR
+\NC width \NC numeric \NC \NC \NC \NR
+\NC height \NC numeric \NC \NC \NC \NR
+\LL
+\stoptabulate
+
+\stopchapter
+
+\stopcomponent
+
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-text.tex b/doc/context/sources/general/manuals/luametafun/luametafun-text.tex
new file mode 100644
index 000000000..4f08ee00f
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-text.tex
@@ -0,0 +1,137 @@
+% language=us
+
+\environment luametafun-style
+
+\startcomponent luametafun-text
+
+\startchapter[title={Text}]
+
+The \METAFUN\ \type {textext} command normally can do the job of typesetting a
+text snippet quite well.
+
+\startbuffer
+\startMPcode
+ fill fullcircle xyscaled (8cm,1cm) withcolor "darkred" ;
+ draw textext("\bf This is text A") withcolor "white" ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+We get:
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+You can use regular \CONTEXT\ commands, so this is valid:
+
+\startbuffer
+\startMPcode
+ fill fullcircle xyscaled (8cm,1cm) withcolor "darkred" ;
+ draw textext("\framed{\bf This is text A}") withcolor "white" ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+Of course you can as well draw a frame in \METAPOST\ but the \type {\framed}
+command has more options, like alignments.
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+Here is a variant using the \METAFUN\ interface:
+
+\startbuffer
+\startMPcode
+ fill fullcircle xyscaled (8cm,1cm) withcolor "darkred" ;
+ draw lmt_text [
+ text = "This is text A",
+ color = "white",
+ style = "bold"
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+The outcome is more or less the same:
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+Here is another example. The \type {format} option is actually why this command
+is provided.
+
+\startbuffer
+\startMPcode
+ fill fullcircle xyscaled (8cm,1cm) withcolor "darkred" ;
+ draw lmt_text [
+ text = decimal 123.45678,
+ color = "white",
+ style = "bold",
+ format = "@0.3F",
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+The following parameters can be set:
+
+\starttabulate[|T|T|T|p|]
+\FL
+\BC name \BC type \BC default \BC comment \NC \NR
+\ML
+\NC offset \NC numeric \NC 0 \NC \NC \NR
+\NC strut \NC string \NC auto \NC adapts the dimensions to the font (\type {yes} uses the the default strut) \NC \NR
+\NC style \NC string \NC \NC \NC \NR
+\NC color \NC string \NC \NC \NC \NR
+\NC text \NC string \NC \NC \NC \NR
+\NC anchor \NC string \NC \NC one of these \type {lft}, \type {urt} like anchors \NC \NR
+\NC format \NC string \NC \NC a format specifier using \type {@} instead of a percent sign \NC \NR
+\NC position \NC pair \NC origin \NC \NC \NR
+\NC trace \NC boolean \NC false \NC \NC \NR
+\LL
+\stoptabulate
+
+The next example demonstrates the positioning options:
+
+\startbuffer
+\startMPcode
+ fill fullcircle xyscaled (8cm,1cm) withcolor "darkblue" ;
+ fill fullcircle scaled .5mm withcolor "white" ;
+ draw lmt_text [
+ text = "left",
+ color = "white",
+ style = "bold",
+ anchor = "lft",
+ position = (-1mm,2mm),
+ ] ;
+ draw lmt_text [
+ text = "right",
+ color = "white",
+ style = "bold",
+ anchor = "rt",
+ offset = 3mm,
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+
+\stopchapter
+
+\stopcomponent
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun-titlepage.tex b/doc/context/sources/general/manuals/luametafun/luametafun-titlepage.tex
new file mode 100644
index 000000000..4450199e6
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun-titlepage.tex
@@ -0,0 +1,41 @@
+
+\environment luametafun-style
+
+\startcomponent luametafun-titlepage
+
+\startMPpage
+
+ fill Page withcolor "darkblue" ;
+
+ path p ; p := (0,0) -- (0,4) -- (2,2) -- (4,4) -- (4,0) ;
+
+ p := p scaled 20; p := p shifted - center p ;
+
+ draw image (
+ for i=1 upto 1000 :
+ draw p shifted (center Page randomized urcorner Page) ;
+ endfor ;
+ ) withcolor "darkgreen" ;
+
+ setbounds currentpicture to Page ;
+
+ draw
+ % textext.ulft("\ss luametafun")
+ textext.ulft("\ss metafun xl")
+ xsized .55bbwidth(Page)
+ shifted lrcorner Page
+ shifted (-15mm,35mm)
+ withcolor "white"
+ ;
+
+ draw
+ textext.ulft("\ss Hans Hagen")
+ xsized .3bbwidth(Page)
+ shifted lrcorner Page
+ shifted (-15mm,15mm)
+ withcolor "white"
+ ;
+
+\stopMPpage
+
+\stopcomponent
diff --git a/doc/context/sources/general/manuals/luametafun/luametafun.tex b/doc/context/sources/general/manuals/luametafun/luametafun.tex
new file mode 100644
index 000000000..0e298dcd0
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/luametafun.tex
@@ -0,0 +1,31 @@
+\setupfootertexts[{\tttf uncorrected draft}]
+
+\environment luametafun-style
+
+\startcomponent luametafun
+
+ \component luametafun-titlepage
+
+ \startfrontmatter
+ \component luametafun-contents
+ \component luametafun-introduction
+ \stopfrontmatter
+
+ \startbodymatter
+ \component luametafun-text
+ \component luametafun-function
+ \component luametafun-contour
+ % \component luametafun-grid
+ \component luametafun-axis
+ \component luametafun-outline
+ \component luametafun-followtext
+ \component luametafun-placeholder
+ \component luametafun-arrow
+ \component luametafun-chart
+ \component luametafun-mesh
+ \component luametafun-shade
+ \component luametafun-svg
+ \component luametafun-interface
+ \stopbodymatter
+
+\stopcomponent
diff --git a/doc/context/sources/general/manuals/luametafun/mozilla-svg-001.svg b/doc/context/sources/general/manuals/luametafun/mozilla-svg-001.svg
new file mode 100644
index 000000000..f74af7dd1
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/mozilla-svg-001.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg"><path fill="#d87512" d="M17.786 44.63c-.606.115-1.23.173-1.854.173-2.444 0-4.644-.864-6.04-2.375-.855-.92-1.394-2.147-1.517-3.47-.126-1.243.067-2.638.58-4.163.325-1.016.83-2.01 1.365-3.064.216-.426.437-.858.65-1.302.702-1.454 1.504-3.164 2.11-5.05.715-2.188.943-4.287.682-6.23-.267-2.102-.994-3.972-1.74-5.685a2.992 2.992 0 0 0-4.15-1.446c-.71.375-1.23 1-1.467 1.77a2.983 2.983 0 0 0 .218 2.292c.632 1.19 1.314 2.596 1.592 3.977.238 1.137.18 2.41-.184 3.897-.37 1.538-.976 3.143-1.522 4.518-.16.406-.33.816-.507 1.234-.507 1.215-1.032 2.47-1.364 3.838-.55 2.14-.666 4.152-.348 5.97.36 2.163 1.41 4.14 2.955 5.567 2.027 1.88 4.808 2.914 7.826 2.914 1.14 0 2.274-.146 3.375-.437l-.66-2.923"/><g fill="#bc600d"><path d="M11.585 43.742s.387 1.248.104 3.05c0 0 2.045-.466 1.898-2.27 0 0-.815-.29-2-.78M9.19 41.484S8.98 42.94 7.93 44.43c0 0 2.103.42 2.774-1.265 0 0-.696-.66-1.515-1.68M8.398 37.21s-.926 1.432-3.23 2.322c0 0 1.514 2.303 3.53.904 0 0-.237-1.388-.3-3.226M12.964 15.833s-1.685.798-3.783 3.45c0 0 2.1 1.55 4.663 2.228 0 0 .285-3.093-.88-5.677M13.5 23.873s-2.988.544-5.57 2.794c0 0 1.615 1.708 3.583 2.62 0 0 1.678-3.39 1.987-5.414M10.32 31.73s-1.483 0-4.483.812c0 0-.01 2.873 2.94 2.823 0 0 .747-1.75 1.544-3.635"/></g><g fill="#d87512"><path d="M53.33 39.37c0-4.484-35.622-4.484-35.622 0 0 10.16.05 10.25 17.81 10.25 17.762 0 17.812-.09 17.812-10.25"/><path d="M42.645 56.04c1.688 2.02 9.275.043 10.504-2.28 5.01-9.482-.006-13.58-.006-13.58l-10.5 1.313s-2.154 11.977 0 14.547"/></g><g fill="#bc600d"><path d="M54.2 41.496s-.336 4.246-4.657 9.573c0 0 4.38-1.7 5.808-4.3 0 0 .448-3.02-1.15-5.274M55.08 48.69s-1.065 1.88-3.563 3.872c0 0 1.78-.03 2.576-.785 0 0 .77-1.41.987-3.086"/></g><path fill="#f29a2e" d="M35.484 60.38c1.87 2.23 8.547 2.09 10.574 0 2.904-2.995 2.78-16.656 2.904-23.314l-12.418-1.053s-3.444 21.52-1.06 24.367"/><g fill="#bc600d"><path d="M48.21 53.53s-3.578-3.443-8.738-.013c0 0 5.754 2.455 7.365 5.672 0 0 1.126-2.245 1.373-5.66M48.775 46.06s-3.852-3.09-7.938 1.43c0 0 4.452-.47 7.632 3.635 0 0 .493-3.05.305-5.065"/></g><g fill="#3e4347"><path d="M43.847 61.57l-.397-2.765 1.344 2.445zM40.41 61.996l.502-3.294.498 3.294zM36.713 61.3l1.317-2.26-.372 2.59z"/></g><path fill="#d87512" d="M28.388 56.04c-1.688 2.02-9.277.043-10.504-2.28-5.01-9.482.004-13.58.004-13.58l10.5 1.313s2.154 11.977 0 14.547"/><g fill="#bc600d"><path d="M16.833 41.496s.336 4.246 4.657 9.573c0 0-4.38-1.7-5.807-4.3 0 0-.448-3.02 1.15-5.274M15.957 48.69s1.066 1.88 3.563 3.872c0 0-1.782-.03-2.576-.785 0 0-.772-1.41-.987-3.086"/></g><path fill="#f29a2e" d="M35.548 60.38c-1.87 2.23-8.548 2.09-10.575 0-2.904-2.995-2.78-16.656-2.904-23.314l12.417-1.053s3.446 21.52 1.06 24.367"/><g fill="#bc600d"><path d="M22.822 53.53s3.58-3.443 8.74-.013c0 0-5.754 2.455-7.367 5.672 0 0-1.125-2.245-1.373-5.66M22.255 46.06s3.852-3.09 7.94 1.43c0 0-4.453-.47-7.633 3.635 0 0-.493-3.05-.307-5.065"/></g><g fill="#3e4347"><path d="M26.24 61.25l1.345-2.445-.395 2.765zM29.62 61.996l.5-3.294.5 3.294zM33.375 61.63L33 59.04l1.32 2.26zM35.516 60.46c-.395-2.48-.482-4.96-.5-7.438.015-2.48.104-4.96.5-7.44.396 2.48.485 4.96.5 7.44-.018 2.48-.106 4.96-.5 7.438"/></g><path fill="#f29a2e" d="M27.777 6.994c0 3.82-2.727 6.987-6.086 6.915C11.83 13.7 15.893 2 15.893 2c3.36 0 11.885 1.176 11.885 4.994"/><path fill="#af5a31" d="M24.05 7.752c0 2.037-1.454 3.727-3.248 3.688-5.26-.11-3.093-6.353-3.093-6.353 1.792 0 6.34.628 6.34 2.665"/><path fill="#f29a2e" d="M43.26 6.994c0 3.82 2.726 6.987 6.086 6.915 9.86-.21 5.8-11.91 5.8-11.91C51.782 2 43.26 3.176 43.26 6.994"/><path fill="#af5a31" d="M46.983 7.752c0 2.037 1.455 3.727 3.247 3.688 5.26-.11 3.094-6.353 3.094-6.353-1.794 0-6.34.628-6.34 2.665"/><path fill="#f29a2e" d="M55.806 33.378c0 7.155-9.517 8.13-20.288 8.13-10.776 0-20.29-.975-20.29-8.13 0-29.96 11.596-29.14 20.29-29.14 8.69 0 20.288-.82 20.288 29.14"/><g fill="#3e4347"><path d="M35.54 7.59c3.24 0 6.15 1.084 8.156 2.81-.77-2.945-4.135-5.16-8.173-5.16-4.06 0-7.442 2.238-8.186 5.204 2.01-1.753 4.938-2.855 8.204-2.855"/><path d="M35.535 11.193c2.217 0 4.21.744 5.584 1.925-.528-2.02-2.835-3.534-5.6-3.534-2.78 0-5.095 1.533-5.605 3.564 1.376-1.198 3.383-1.955 5.62-1.955"/></g><path fill="#ffe8bb" d="M29.553 43.727l-18.408-7.01 4.24-9.06s2.704 3.85 13.29 6.82l.878 9.243"/><path fill="#3e4347" d="M29.37 39.77c-7.462-1.27-16.325-6.673-16.48-6.75l.992-2.168c.184.092 8.806 5.342 15.853 6.544l-.366 2.374"/><path fill="#ffe8bb" d="M41.48 43.727l18.406-7.01-4.24-9.06s-2.704 3.85-13.29 6.82l-.876 9.243"/><path fill="#3e4347" d="M41.663 39.77c7.46-1.27 16.325-6.673 16.48-6.75l-.993-2.168c-.184.092-8.808 5.342-15.852 6.544l.365 2.374"/><g fill="#ffe8bb"><path d="M43.524 45.57C38.752 42.023 41.4 33.86 41.4 33.86c-5.657 5.906-12.662 8.74-12.662 8.74 1.608 5.446 5.77 6.412 5.77 6.412-.34-1.835.663-3.302.663-3.302 1.68 2.22 5.03 2.986 5.03 2.986-1.287-1.508-.948-3.835-.948-3.835 2.326.875 4.27.71 4.27.71"/><path d="M42.29 42.97c-2.634 2.247-10.917 2.247-13.553 0-2.856-2.435-2.495-7.144.1-9.884 2.397-2.527 10.958-2.527 13.355 0 2.595 2.74 2.956 7.45.098 9.883"/></g><g fill="#3e4347"><path d="M36.18 40.48a.69.69 0 0 1-.644-.477c-.227-.67-.77-3.293-.71-5.498.01-.398.325-.71.7-.698.38.01.674.343.663.74-.057 2.01.46 4.466.633 4.974.127.375-.06.786-.414.92a.607.607 0 0 1-.23.04"/><path d="M30.504 43.25c.21-.202.394-.408.582-.61.188-.204.378-.405.57-.604.385-.396.782-.78 1.2-1.145a14.125 14.125 0 0 1 2.745-1.9c.504-.263 1.032-.49 1.59-.654s1.153-.273 1.772-.253c.31.01.623.055.928.146.307.088.602.23.86.416.263.19.485.422.652.684.17.257.287.54.35.83a4.247 4.247 0 0 0-.677-.448 2.567 2.567 0 0 0-.68-.237c-.447-.088-.887-.04-1.33.06-.89.216-1.786.65-2.69 1.114-.905.466-1.818.983-2.776 1.466-.48.24-.97.473-1.48.682-.256.103-.517.202-.783.285-.27.078-.546.155-.833.167"/></g><path fill="#f15a61" d="M41.34 31.743c-1.17-.528-4.757-.57-5.83-.57-1.07 0-4.66.042-5.83.57-.832.376-.187 1.31 2.027 2.116 1.397.506 2.733.666 3.803.666 1.07 0 2.405-.16 3.805-.667 2.213-.808 2.856-1.74 2.025-2.117"/><g fill="#3e4347"><path d="M29.917 23.48l1.61 5.292L26.954 26z"/><path d="M22.645 31.828c-.522 0-.932-.056-1.17-.098-2.986-.52-4.632-1.996-6.09-4.067l.185-2.472c1.52 1.446 3.953 3.76 6.28 4.167 1.156.2 2.853-.016 4.15-1.234 1.537-1.44 2.263-4.05 2.1-7.547l1.635.132c.2 4.312-.116 7.244-2.212 9.212-1.692 1.59-3.613 1.908-4.878 1.908M41.12 23.48l-1.613 5.292L44.08 26z"/><path d="M48.39 31.828c.52 0 .93-.056 1.167-.098 2.99-.52 4.637-1.996 6.09-4.067l-.182-2.472c-1.52 1.446-3.955 3.76-6.28 4.167-1.156.2-2.855-.016-4.154-1.234-1.532-1.44-2.258-4.05-2.095-7.547l-1.636.132c-.202 4.312.114 7.244 2.213 9.212 1.69 1.59 3.61 1.908 4.877 1.908"/></g><path fill="#ffe8bb" d="M30.25 22.09c-.852 5.282-3.728 5.87-6.696 5.577-2.986-.294-5.396-2.667-5.396-6.743 0-4.28 0-4.28 6.647-5.752 6.728-1.49 6 3.437 5.445 6.918"/><path fill="#3e4347" d="M29.16 22.547c-.244 2.534-2.61 4.357-5.287 4.072-2.674-.286-4.645-2.57-4.402-5.102s.28-2.75 5.108-2.237c4.83.514 4.824.737 4.582 3.267"/><path fill="#fff" d="M27.59 21.884c-.16 1.688-1.74 2.903-3.522 2.714-1.785-.19-3.096-1.712-2.936-3.4.163-1.69.186-1.835 3.406-1.493 3.22.344 3.215.49 3.053 2.18"/><g fill="#3e4347"><ellipse cx="25.5" cy="21.08" rx="1.45" ry="1.647"/><path d="M31.27 17.896c.42 0 .807-.284.936-.728.16-.546-.133-1.122-.65-1.29l-5.98-1.924c-.516-.166-1.065.14-1.225.685-.155.543.136 1.12.652 1.287l5.98 1.924a.95.95 0 0 0 .288.046"/></g><path fill="#ffe8bb" d="M40.78 22.09c.855 5.282 3.73 5.87 6.7 5.577 2.984-.294 5.395-2.667 5.395-6.743 0-4.28-.002-4.28-6.646-5.752-6.73-1.49-6.01 3.437-5.45 6.918"/><path fill="#3e4347" d="M41.873 22.547c.243 2.534 2.61 4.357 5.287 4.072 2.674-.286 4.646-2.57 4.402-5.102-.242-2.533-.28-2.75-5.107-2.237-4.83.514-4.824.737-4.582 3.267"/><path fill="#fff" d="M43.44 21.884c.16 1.688 1.737 2.903 3.522 2.714 1.783-.19 3.098-1.712 2.936-3.4-.16-1.69-.188-1.835-3.404-1.493-3.22.344-3.217.49-3.054 2.18"/><g fill="#3e4347"><ellipse cx="47.722" cy="20.932" rx="1.45" ry="1.647"/><path d="M39.76 17.896a.982.982 0 0 1-.935-.728c-.16-.546.132-1.122.65-1.29l5.98-1.924c.517-.166 1.063.14 1.224.685.155.543-.136 1.12-.653 1.287l-5.98 1.924a.96.96 0 0 1-.287.046"/></g></svg>
diff --git a/doc/context/sources/general/manuals/luametafun/mozilla-svg-002.svg b/doc/context/sources/general/manuals/luametafun/mozilla-svg-002.svg
new file mode 100644
index 000000000..ac49c531a
--- /dev/null
+++ b/doc/context/sources/general/manuals/luametafun/mozilla-svg-002.svg
@@ -0,0 +1,10 @@
+<svg id="glyph651" xmlns="http://www.w3.org/2000/svg"><g fill="#42cde2">
+ <path d="
+ M18.06 50.947
+ l-4.45 3.732.136-9.223
+ a.623.623 0 0 0-.146-.408
+ L11.63 42.7a.62.62 0 0 0-.873-.077
+ l-8.54 7.165
+ a.62.62 0 0 0-.076.874l1.93 2.3a.62.62 0 0 0 .875.074l3.948-3.313-.058 9.45c-.002.147.05.29.144.403l1.845 2.2a.62.62 0 0 0 .874.075l9.102-7.64a.62.62 0 0 0 .077-.874l-1.946-2.318a.623.623 0 0 0-.874-.075M36.03 33.53l-6.464 3.732 2.23-11.648a.812.812 0 0 0-.095-.548l-1.97-3.412a.8.8 0 0 0-1.09-.293l-12.41 7.166a.798.798 0 0 0-.292 1.09l1.928 3.34a.798.798 0 0 0 1.09.293l5.74-3.314-2.184 11.95a.794.794 0 0 0 .093.544l1.846 3.197a.797.797 0 0 0 1.09.29l13.234-7.637a.796.796 0 0 0 .292-1.09l-1.945-3.37a.8.8 0 0 0-1.09-.29M59.33 16.792l-7.794 2.837 4.686-12.316a.894.894 0 0 0 .003-.618l-1.497-4.114a.885.885 0 0 0-1.135-.53L38.63 7.5a.886.886 0 0 0-.53 1.136l1.466 4.03a.884.884 0 0 0 1.136.53l6.92-2.52-4.695 12.658a.888.888 0 0 0-.003.612l1.402 3.854a.888.888 0 0 0 1.137.53l15.953-5.806a.887.887 0 0 0 .53-1.137l-1.48-4.06a.887.887 0 0 0-1.136-.532"/>
+ </g>
+ </svg>
diff --git a/doc/context/sources/general/manuals/metafun/metafun-basics.tex b/doc/context/sources/general/manuals/metafun/metafun-basics.tex
index df556e239..a52c7a35a 100644
--- a/doc/context/sources/general/manuals/metafun/metafun-basics.tex
+++ b/doc/context/sources/general/manuals/metafun/metafun-basics.tex
@@ -2835,7 +2835,7 @@ eofill fullsquare rotated 45 scaled 2cm
\typebuffer
The \type {eofill} is a \METAFUN\ extension. Hopefully the next explains a bit
-how this works (you can find explanations zon the internet).
+how this works (you can find explanations on the Internet).
\startlinecorrection[blank]
\processMPbuffer
diff --git a/metapost/context/base/mpiv/mp-blob.mpiv b/metapost/context/base/mpiv/mp-blob.mpiv
index d1fc7357b..dd147bede 100644
--- a/metapost/context/base/mpiv/mp-blob.mpiv
+++ b/metapost/context/base/mpiv/mp-blob.mpiv
@@ -51,8 +51,8 @@ vardef followtext(expr pth, txt) =
image (
mfun_blob_n := mfun_blob_n + 1 ;
lua.mp.mf_inject_blob(mfun_blob_n,txt);
- save pat, al, at, pl, pc, wid, pos, ap, ad, pic, len, n, sc ;
- path pat ; pat := pth ;
+ save pat, al, at, pl, pc, wid, pos, ap, ad, pic, len, n, b, sc ;
+ path pat, b ; pat := pth ;
numeric al, at, pl, pc, wid, pos, len[], n, sc ;
pair ap, ad ;
picture pic[] ;
@@ -98,15 +98,16 @@ vardef followtext(expr pth, txt) =
draw ap withpen pencircle scaled .50pt withcolor green ;
fi ;
endfor ;
- if ((autoscaleupfollowtext = 2) or (autoscaledownfollowtext = 2)) and
- (sc <> 0) and (sc <> 1):
+ if ((autoscaleupfollowtext = 2) or (autoscaledownfollowtext = 2)) and (sc <> 0) and (sc <> 1):
currentpicture := currentpicture scaled (1/sc) ;
fi ;
+ b := boundingbox currentpicture ;
if tracingfollowtext = 1 :
- draw boundingbox currentpicture withpen pencircle scaled .25pt withcolor blue ;
+ draw b withpen pencircle scaled .25pt withcolor blue ;
fi ;
draw fullcircle scaled 100bp
withprescript "mf_object=followtext"
withprescript "ft_category=" & decimal mfun_blob_n ;
+ setbounds currentpicture to b ;
)
enddef ;
diff --git a/metapost/context/base/mpiv/mp-lmtx.mpxl b/metapost/context/base/mpiv/mp-lmtx.mpxl
index fe511b74f..359c60921 100644
--- a/metapost/context/base/mpiv/mp-lmtx.mpxl
+++ b/metapost/context/base/mpiv/mp-lmtx.mpxl
@@ -247,7 +247,7 @@ vardef lmt_do_axis =
enddef ;
presetparameters "outline" [
- content = "",
+ text = "",
kind = "draw",
fillcolor = "",
drawcolor = "",
@@ -273,22 +273,24 @@ vardef lmt_do_outline =
kind := "b" ;
elseif kind = "reverse" :
kind := "r" ;
+ elseif kind = "fillup" :
+ kind := "u" ;
fi ;
currentoutlinetext := currentoutlinetext + 1 ;
lua.mp.mf_outline_text(
currentoutlinetext,
if align = "" :
- getparameter "outline" "content",
+ getparameter "outline" "text",
else :
"\framed[align={" & align & "}"
if width > 0 :
& ",width=" & decimal width & "bp"
fi
if style <> "" :
- & ",foregroundstyle=" & style
+ & ",foregroundstyle={" & style & "}"
fi
& ",offset=none,frame=off]{"
- & (getparameter "outline" "content")
+ & (getparameter "outline" "text")
& "}",
fi,
kind
@@ -310,7 +312,7 @@ vardef lmt_do_outline =
withcolor getparameter "outline" "drawcolor"
);
elseif kind = "u" :
- mfun_do_outline_text_set_f (
+ mfun_do_outline_text_set_u (
withcolor getparameter "outline" "fillcolor"
);
elseif kind = "r" :
@@ -331,7 +333,7 @@ vardef lmt_do_outline =
enddef ;
presetparameters "followtext" [
- content = "",
+ text = "",
spread = true,
trace = false,
reverse = false,
@@ -344,24 +346,28 @@ def lmt_followtext = applyparameters "followtext" "lmt_do_followtext" enddef ;
vardef lmt_do_followtext =
image (
- save s_u ; string s_u ; s_u := getparameter "followtext" "autoscaleup" ;
- save s_d ; string s_d ; s_d := getparameter "followtext" "autoscaledown" ;
- save followtextalternative ; followtextalternative := if getparameter "followtext" "spread" : 1 else : 0 fi ;
- save tracingfollowtext ; tracingfollowtext := if getparameter "followtext" "trace" : 1 else : 0 fi ;
+ pushparameters "followtext" ;
+ save s_u ; string s_u ; s_u := getparameter "autoscaleup" ;
+ save s_d ; string s_d ; s_d := getparameter "autoscaledown" ;
+ save followtextalternative ; followtextalternative := if getparameter "spread" : 1 else : 0 fi ;
+ save tracingfollowtext ; tracingfollowtext := if getparameter "trace" : 1 else : 0 fi ;
save autoscaleupfollowtext ; autoscaleupfollowtext := if s_u = "yes" : 1 elseif s_u = "max" : 2 else : 0 fi ;
save autoscaledownfollowtext ; autoscaledownfollowtext := if s_d = "yes" : 1 elseif s_d = "max" : 2 else : 0 fi ;
draw followtext (
- if (getparameter "followtext" "reverse") : reverse fi getparameter "followtext" "path",
- getparameter "followtext" "content"
+ if (getparameter "reverse") : reverse fi (getparameter "path"),
+ getparameter "text"
) ;
+ popparameters ;
)
enddef ;
presetparameters "arrow" [
path = origin,
+ % pen = ...,
kind = "fill",
dimple = 1/5,
scale = 3/4,
+ penscale = 3,
length = 4,
angle = 45,
location = "end", % middle both
@@ -374,23 +380,32 @@ def lmt_arrow = applyparameters "arrow" "lmt_do_arrow" enddef ;
vardef lmt_do_arrow =
image (
- save a ; string a ; a := getparameter "arrow" "alternative" ;
- save l ; string l ; l := getparameter "arrow" "location" ;
- save k ; string k ; k := getparameter "arrow" "kind" ;
- save p ; path p ; p := getparameter "arrow" "path" ;
+ pushparameters "arrow" ;
+ save a ; string a ; a := getparameter "alternative" ;
+ save l ; string l ; l := getparameter "location" ;
+ save k ; string k ; k := getparameter "kind" ;
+ save p ; path p ; p := getparameter "path" ;
save ahvariant ; ahvariant := if a = "dimpled" : 1 elseif a = "curved" : 2 else : 0 fi ;
- save ahdimple ; ahdimple := getparameter "arrow" "dimple" ;
- save ahscale ; ahscale := getparameter "arrow" "scale" ;
- save ahangle ; ahangle := getparameter "arrow" "angle" ;
- save ahlength ; ahlength := getparameter "arrow" "length" ;
- if not getparameter "arrow" "headonly" :
+ save ahdimple ; ahdimple := getparameter "dimple" ;
+ save ahscale ; ahscale := getparameter "scale" ;
+ save ahangle ; ahangle := getparameter "angle" ;
+ save ahlength ; ahlength := getparameter "length" ;
+ if not getparameter "headonly" :
draw p ;
fi ;
+ if hasparameter "pen" :
+ % a cheat: we should have a type check in lua
+ if hasoption "pen" "auto" :
+ ahlength := (getparameter "penscale") * boundingradius(currentpen) ;
+ else :
+ ahlength := (getparameter "penscale") * boundingradius(getparameterpen "pen") ;
+ fi ;
+ fi ;
if k = "draw" : draw elseif k = "both" : filldraw else : fill fi
if l = "middle" :
midarrowhead p ;
elseif l = "percentage" :
- arrowheadonpath (p, (getparameter "arrow" "percentage")/100) ;
+ arrowheadonpath (p, (getparameter "percentage")/100) ;
elseif l = "both" :
arrowhead p ;
if k = "draw" : draw elseif k = "both" : filldraw else : fill fi
@@ -398,6 +413,7 @@ vardef lmt_do_arrow =
else :
arrowhead p ;
fi ;
+ popparameters ;
)
enddef ;
@@ -415,13 +431,14 @@ def lmt_placeholder = applyparameters "placeholder" "lmt_do_placeholder" enddef
def lmt_do_placeholder =
begingroup ;
+ pushparameters "placeholder" ;
save w, h, d, r, p, c, b, s, q, a ;
numeric w, h, d, r ; path p ; string s, a ;
- s := getparameter "placeholder" "color" ;
- w := getparameter "placeholder" "width" ;
- h := getparameter "placeholder" "height" ;
- r := getparameter "placeholder" "reduction" ;
- a := getparameter "placeholder" "alternative" ;
+ s := getparameter "color" ;
+ w := getparameter "width" ;
+ h := getparameter "height" ;
+ r := getparameter "reduction" ;
+ a := getparameter "alternative" ;
d := max(w,h) ;
if cmykcolor resolvedcolor(s) :
cmykcolor c, b ; b := (0,0,0,0)
@@ -445,6 +462,7 @@ def lmt_do_placeholder =
withcolor r[c randomized(.3,.9),b] ;
endfor ;
clip currentpicture to p ;
+ popparameters ;
endgroup ;
enddef ;
@@ -1239,7 +1257,8 @@ vardef lmt_do_chart_bar =
)
enddef ;
-% more complex than needed but i want to trace so i need the vars
+%D This one is more complex than needed but I want to trace so I need all those
+%D variables.
presetparameters "shade" [
alternative = "circular",
@@ -1258,6 +1277,8 @@ presetparameters "shade" [
] ;
+% TODO: pass colors as strings
+
def lmt_shade = applyparameters "shade" "lmt_do_shade" enddef ;
vardef lmt_do_shade =
@@ -1318,27 +1339,10 @@ vardef lmt_do_shade =
center_b := getparameter "origin" 2 ;
fi ;
fi
- if hasparameter "vector" :
- center_a := point (getparameter "vector" 1) of mfun_shade_path ;
- center_b := point (getparameter "vector" 2) of mfun_shade_path ;
- fi
if hasparameter "colors" :
color_a := getparameter "colors" 1 ;
color_b := getparameter "colors" 2 ;
fi
- if hasparameter "center" :
- if numeric getparameter "center" :
- center_a := center mfun_shade_path shifted (
- (getparameter "center") * bbwidth (mfun_shade_path)/2,
- (getparameter "center") * bbheight(mfun_shade_path)/2
- ) ;
- else :
- center_a := center mfun_shade_path shifted (
- (getparameter "center" 1) * bbwidth (mfun_shade_path)/2,
- (getparameter "center" 2) * bbheight(mfun_shade_path)/2
- ) ;
- fi
- fi
if hasparameter "direction" :
save a, b, bb ; path bb ;
bb := boundingbox(mfun_shade_path) ;
@@ -1369,6 +1373,26 @@ vardef lmt_do_shade =
center_b := point p_b of bb ;
fi
fi ;
+ if hasparameter "center" :
+ save cx, cy ;
+ if numeric getparameter "center" :
+ cx := getparameter "center" ;
+ cx := cy ;
+ % elseif pair getparameter "center" :
+ % cx := xpart getparameter "center" ;
+ % cy := ypart getparameter "center" ;
+ else :
+ cx := getparameter "center" 1 ;
+ cy := getparameter "center" 2 ;
+ fi
+ center_a := center mfun_shade_path shifted (
+ cx * bbwidth (mfun_shade_path)/2,
+ cy * bbheight(mfun_shade_path)/2
+ ) ;
+ elseif hasparameter "vector" :
+ center_a := point (getparameter "vector" 1) of mfun_shade_path ;
+ center_b := point (getparameter "vector" 2) of mfun_shade_path ;
+ fi
fill mfun_shade_path
withprescript "sh_domain=" & decimal domain_min & " " & decimal domain_max
withprescript "sh_transform=yes"
@@ -1380,12 +1404,14 @@ vardef lmt_do_shade =
withprescript "sh_set_y=" & ddecimal (mfun_shade_ny,mfun_shade_ly) %
if alternative = "linear" :
withprescript "sh_type=linear"
- withprescript "sh_factor=1"
+ % withprescript "sh_factor=1"
+ withprescript "sh_factor=" & decimal factor
withprescript "sh_center_a=" & ddecimal center_a
withprescript "sh_center_b=" & ddecimal center_b
else :
withprescript "sh_type=circular"
- withprescript "sh_factor=1.2"
+ % withprescript "sh_factor=1.2"
+ withprescript "sh_factor=" & decimal factor
withprescript "sh_center_a=" & ddecimal center_a
withprescript "sh_center_b=" & ddecimal center_b
withprescript "sh_radius_a=" & decimal radius_a
@@ -1394,11 +1420,13 @@ vardef lmt_do_shade =
if getparameter "trace" :
draw fullcircle scaled 1mm shifted center_a ;
draw fullsquare scaled 2mm shifted center_b ;
- draw textext.top("\strut\ttx center a") scaled .2 shifted center_a shifted (0, 2mm) ;
- draw textext.bot("\strut\ttx center b") scaled .2 shifted center_b shifted (0,-2mm) ;
+ draw textext.top("\strut\ttx center a") ysized LineHeight shifted center_a shifted (0, 2mm) ;
+ draw textext.bot("\strut\ttx center b") ysized LineHeight shifted center_b shifted (0,-2mm) ;
if alternative = "circular" :
- draw fullcircle scaled ( radius_a * 2) shifted center_a dashed evenly ;
- draw fullcircle scaled (factor * radius_b * 2) shifted -center_b dashed evenly ;
+% draw fullcircle scaled ( radius_a * 2) shifted center_a dashed evenly ;
+% draw fullcircle scaled (factor * radius_b * 2) shifted -center_b dashed evenly ;
+ draw fullcircle scaled ( radius_a) shifted center_a dashed evenly ;
+ draw fullcircle scaled (factor * radius_b) shifted -center_b dashed evenly ;
fi
fi
popparameters ;
@@ -1473,12 +1501,15 @@ boolean lmx_contour_loaded ; lmx_contour_loaded := false ;
def mfun_only_draw = addto currentpicture doublepath enddef ;
def mfun_only_fill = addto currentpicture contour enddef ;
def mfun_only_nodraw text t = addto currentpicture doublepath t withpostscript "collect" enddef ;
-def mfun_only_nofill text t = addto currentpicture contour t withpostscript "collect" enddef ;
+def mfun_only_nofill text t = addto currentpicture contour t withpostscript "evenodd" enddef ;
+def mfun_only_eofill text t = addto currentpicture contour t withpostscript "collect" enddef ;
def lmt_do_contour_shortcuts =
save D ; let D = mfun_only_draw ;
+ save E ; let E = mfun_only_eofill ;
save F ; let F = mfun_only_fill ;
save d ; let d = mfun_only_nodraw ;
+ save e ; let f = mfun_only_eofill ;
save f ; let f = mfun_only_nofill ;
save C ; let C = cycle ;
enddef ;
@@ -1975,3 +2006,25 @@ vardef lmt_do_contour =
)
enddef ;
+presetparameters "svg" [
+ filename = "",
+ width = 0,
+ height = 0,
+] ;
+
+def lmt_svg = applyparameters "svg" "lmt_do_svg" enddef ;
+
+vardef lmt_do_svg =
+ save w, h ;
+ w := getparameter "svg" "width" ;
+ h := getparameter "svg" "height" ;
+ image (
+ lua.mp.lmt_svg_include()
+ )
+ if w > 0 :
+ if h > 0 : xysized(w,h) else : xsized(w) fi
+ else :
+ if h > 0 : ysized(h) fi
+ fi
+enddef ;
+
diff --git a/metapost/context/base/mpiv/mp-luas.mpxl b/metapost/context/base/mpiv/mp-luas.mpxl
index cd6733f84..1cd5efe7f 100644
--- a/metapost/context/base/mpiv/mp-luas.mpxl
+++ b/metapost/context/base/mpiv/mp-luas.mpxl
@@ -209,6 +209,7 @@ newinternal mfid_getparameterdefault ; mfid_getparameterdefault := scriptindex
newinternal mfid_getparametercount ; mfid_getparametercount := scriptindex "getparametercount" ;
newinternal mfid_getmaxparametercount ; mfid_getmaxparametercount := scriptindex "getmaxparametercount" ;
newinternal mfid_getparameterpath ; mfid_getparameterpath := scriptindex "getparameterpath" ;
+newinternal mfid_getparameterpen ; mfid_getparameterpen := scriptindex "getparameterpen" ;
newinternal mfid_getparametertext ; mfid_getparametertext := scriptindex "getparametertext" ;
%%%%%%%%%%% mfid_getparameteroption ; mfid_getparameteroption := scriptindex "getparameteroption" ;
newinternal mfid_applyparameters ; mfid_applyparameters := scriptindex "applyparameters" ;
@@ -224,6 +225,7 @@ def getparameterdefault = runscript mfid_getparameterdefault enddef ;
def getparametercount = runscript mfid_getparametercount enddef ;
def getmaxparametercount = runscript mfid_getmaxparametercount enddef ;
def getparameterpath = runscript mfid_getparameterpath enddef ;
+def getparameterpen = runscript mfid_getparameterpen enddef ;
def getparametertext = runscript mfid_getparametertext enddef ;
%%% getparameteroption = runscript mfid_getparameteroption enddef ;
def applyparameters = runscript mfid_applyparameters enddef ;
diff --git a/metapost/context/base/mpiv/mp-mlib.mpiv b/metapost/context/base/mpiv/mp-mlib.mpiv
index 7d20da104..286e4e495 100644
--- a/metapost/context/base/mpiv/mp-mlib.mpiv
+++ b/metapost/context/base/mpiv/mp-mlib.mpiv
@@ -704,6 +704,15 @@ vardef mfun_max_radius(expr p) =
)
enddef ;
+vardef mfun_min_radius(expr p) =
+ min (
+ (xpart center p - xpart llcorner p) ++ (ypart center p - ypart llcorner p),
+ (xpart center p - xpart ulcorner p) ++ (ypart ulcorner p - ypart center p),
+ (xpart lrcorner p - xpart center p) ++ (ypart center p - ypart lrcorner p),
+ (xpart urcorner p - xpart center p) ++ (ypart urcorner p - ypart center p)
+ )
+enddef ;
+
primarydef p withshademethod m =
hide(mfun_with_shade_method_analyze(p))
p
@@ -1149,6 +1158,10 @@ vardef mfun_do_outline_text_set_f (text f) text r =
def mfun_do_outline_options_r = r enddef ;
enddef ;
+vardef mfun_do_outline_text_set_u (text f) text r =
+ def mfun_do_outline_options_f = f enddef ;
+enddef ;
+
vardef mfun_do_outline_text_set_d (text d) text r =
def mfun_do_outline_options_d = d enddef ;
def mfun_do_outline_options_r = r enddef ;
@@ -1682,7 +1695,12 @@ def nofill text t = fill t withpostscript "collect" enddef ;
def nodraw text t = draw t withpostscript "collect" enddef ;
def dodraw text t = draw t withpostscript "flush" enddef ;
def dofill text t = fill t withpostscript "flush" enddef ;
-%%% eoclip text t = clip t withpostscript "evenodd" enddef ; % no postscripts yet
+
+if contextlmtxmode :
+ def eoclip text t = clip t withpostscript "evenodd" enddef ;
+else :
+ def eoclip text t = clip t enddef ; % no postscripts yet
+fi ;
% def withrule expr r =
% if (t = "even-odd") or (t = "evenodd") : withpostscript "evenodd" fi
diff --git a/metapost/context/base/mpiv/mp-tool.mpiv b/metapost/context/base/mpiv/mp-tool.mpiv
index 3bae73aad..b05adcc8b 100644
--- a/metapost/context/base/mpiv/mp-tool.mpiv
+++ b/metapost/context/base/mpiv/mp-tool.mpiv
@@ -886,6 +886,13 @@ vardef roundedsquare (expr width, height, offset) =
(0,height-offset) -- (0,offset) {down} .. cycle
enddef ;
+vardef roundedsquarexy (expr width, height, dx, dy) =
+ (dx,0) -- (width-dx,0) {right} ..
+ (width,dy) -- (width,height-dy) {up} ..
+ (width-dx,height) -- (dx,height) {left} ..
+ (0,height-dy) -- (0,dy) {down} .. cycle
+enddef ;
+
%D Some colors.
def resolvedcolor(expr s) =
@@ -3641,11 +3648,29 @@ enddef ;
vardef totransform(expr x, y, xx, xy, yx, yy) =
save t ; transform t ;
- xpart t = x ;
- ypart t = y ;
- xxpart t = xx ;
- xypart t = xy ;
- yxpart t = yx ;
- yypart t = yy ;
+ xxpart t = xx ; yypart t = yy ;
+ xypart t = xy ; yxpart t = yx ;
+ xpart t = x ; ypart t = y ;
+ t
+enddef ;
+
+vardef bymatrix(expr rx, sx, sy, ry, tx, ty) =
+ save t ; transform t ;
+ xxpart t = rx ; yypart t = ry ;
+ xypart t = sx ; yxpart t = sy ;
+ xpart t = tx ; ypart t = ty ;
t
enddef ;
+
+let xslanted = slanted ;
+
+def yslanted primary s =
+ transformed
+ begingroup
+ save t ; transform t ;
+ xxpart t = 1 ; yypart t = 1 ;
+ xypart t = 0 ; yxpart t = s ;
+ xpart t = 0 ; ypart t = 0 ;
+ t
+ endgroup
+enddef ;
diff --git a/scripts/context/lua/mtx-install.lua b/scripts/context/lua/mtx-install.lua
index 49a912291..fee1ce4d6 100644
--- a/scripts/context/lua/mtx-install.lua
+++ b/scripts/context/lua/mtx-install.lua
@@ -90,34 +90,39 @@ local platforms = {
--
["linux-armhf"] = "linux-armhf",
--
- ["freebsd"] = "freebsd",
+ ["openbsd"] = "openbsd6.5",
+ ["openbsd-i386"] = "openbsd6.5",
+ ["openbsd-amd64"] = "openbsd6.5-amd64",
--
+ ["freebsd"] = "freebsd",
+ ["freebsd-i386"] = "freebsd",
["freebsd-amd64"] = "freebsd-amd64",
--
- ["kfreebsd"] = "kfreebsd-i386",
- ["kfreebsd-i386"] = "kfreebsd-i386",
- --
- ["kfreebsd-amd64"] = "kfreebsd-amd64",
+ -- ["kfreebsd"] = "kfreebsd-i386",
+ -- ["kfreebsd-i386"] = "kfreebsd-i386",
+ -- ["kfreebsd-amd64"] = "kfreebsd-amd64",
--
- ["linux-ppc"] = "linux-ppc",
- ["ppc"] = "linux-ppc",
+ -- ["linux-ppc"] = "linux-ppc",
+ -- ["ppc"] = "linux-ppc",
--
- ["osx"] = "osx-intel",
- ["macosx"] = "osx-intel",
- ["osx-intel"] = "osx-intel",
- ["osxintel"] = "osx-intel",
+ -- ["osx"] = "osx-intel",
+ -- ["macosx"] = "osx-intel",
+ -- ["osx-intel"] = "osx-intel",
+ -- ["osxintel"] = "osx-intel",
--
- ["osx-ppc"] = "osx-ppc",
- ["osx-powerpc"] = "osx-ppc",
- ["osxppc"] = "osx-ppc",
- ["osxpowerpc"] = "osx-ppc",
+ -- ["osx-ppc"] = "osx-ppc",
+ -- ["osx-powerpc"] = "osx-ppc",
+ -- ["osxppc"] = "osx-ppc",
+ -- ["osxpowerpc"] = "osx-ppc",
--
+ ["macosx"] = "osx-64",
+ ["osx"] = "osx-64",
["osx-64"] = "osx-64",
--
- ["solaris-intel"] = "solaris-intel",
+ -- ["solaris-intel"] = "solaris-intel",
--
- ["solaris-sparc"] = "solaris-sparc",
- ["solaris"] = "solaris-sparc",
+ -- ["solaris-sparc"] = "solaris-sparc",
+ -- ["solaris"] = "solaris-sparc",
--
["unknown"] = "unknown",
}
diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua
index 997fbcaec..1f7be7b72 100644
--- a/scripts/context/lua/mtxrun.lua
+++ b/scripts/context/lua/mtxrun.lua
@@ -8407,7 +8407,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["util-sac"] = package.loaded["util-sac"] or true
--- original size: 11332, stripped down to: 8420
+-- original size: 12898, stripped down to: 9275
if not modules then modules={} end modules ['util-sac']={
version=1.001,
@@ -8442,6 +8442,7 @@ end
function streams.size(f)
return f and f[3] or 0
end
+streams.getsize=streams.size
function streams.setposition(f,i)
if f[4] then
if i<=0 then
@@ -8826,6 +8827,50 @@ else
return t
end
end
+do
+ local files=utilities.files
+ if files then
+ local openfile=files.open
+ local openstream=streams.open
+ local openstring=streams.openstring
+ local setmetatable=setmetatable
+ function io.newreader(str,method)
+ local f,m
+ if method=="string" then
+ f=openstring(str)
+ m=streams
+ elseif method=="stream" then
+ f=openstream(str)
+ m=streams
+ else
+ f=openfile(str,"rb")
+ m=files
+ end
+ if f then
+ local t={}
+ setmetatable(t,{
+ __index=function(t,k)
+ local r=m[k]
+ if k=="close" then
+ if f then
+ m.close(f)
+ f=nil
+ end
+ return function() end
+ elseif r then
+ local v=function(_,a,b) return r(f,a,b) end
+ t[k]=v
+ return v
+ else
+ print("unknown key",k)
+ end
+ end
+ } )
+ return t
+ end
+ end
+ end
+end
end -- of closure
@@ -14119,7 +14164,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["util-lua"] = package.loaded["util-lua"] or true
--- original size: 6778, stripped down to: 4699
+-- original size: 7149, stripped down to: 4997
if not modules then modules={} end modules ['util-lua']={
version=1.001,
@@ -14144,6 +14189,11 @@ luautilities.nofstrippedchunks=0
luautilities.nofstrippedbytes=0
local strippedchunks={}
luautilities.strippedchunks=strippedchunks
+if not LUATEXENGINE then
+ LUATEXENGINE=status.luatex_engine and string.lower(status.luatex_engine)
+ JITSUPPORTED=LUATEXENGINE=="luajittex" or jit
+ CONTEXTLMTXMODE=CONTEXTLMTXMODE or (LUATEXENGINE=="luametatex" and 1) or 0
+end
luautilities.suffixes={
tma="tma",
tmc=(CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 and "tmd") or (jit and "tmb") or "tmc",
@@ -14153,7 +14203,7 @@ luautilities.suffixes={
luv="luv",
luj="luj",
tua="tua",
- tuc="tuc",
+ tuc=(CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 and "tud") or (jit and "tub") or "tuc",
}
local function register(name)
if tracestripping then
@@ -25733,8 +25783,8 @@ end -- of closure
-- used libraries : l-bit32.lua l-lua.lua l-macro.lua l-sandbox.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-sha.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua util-soc-imp-reset.lua util-soc-imp-socket.lua util-soc-imp-copas.lua util-soc-imp-ltn12.lua util-soc-imp-mime.lua util-soc-imp-url.lua util-soc-imp-headers.lua util-soc-imp-tp.lua util-soc-imp-http.lua util-soc-imp-ftp.lua util-soc-imp-smtp.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-tpl.lua util-sbx.lua util-mrg.lua util-env.lua luat-env.lua util-zip.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua
-- skipped libraries : -
--- original bytes : 1027480
--- stripped bytes : 407301
+-- original bytes : 1029417
+-- stripped bytes : 408085
-- end library merge
diff --git a/scripts/context/stubs/install/first-setup.sh b/scripts/context/stubs/install/first-setup.sh
index 71fe32c16..83af6a97e 100644
--- a/scripts/context/stubs/install/first-setup.sh
+++ b/scripts/context/stubs/install/first-setup.sh
@@ -48,7 +48,7 @@ case "$system" in
# a) binutils, this should work almost everywhere
if $(which readelf >/dev/null 2>&1); then
readelf -A /proc/self/exe | grep -q '^ \+Tag_ABI_VFP_args'
- if [ ! $? ]; then
+ if [ $? != 0 ]; then
platform="linux-armel"
fi
# b) debian-specific fallback
@@ -86,16 +86,18 @@ case "$system" in
esac ;;
# OpenBSD
OpenBSD)
+ version=`uname -r`
case "$cpu" in
- i*86) platform="openbsd" ;;
- amd64) platform="openbsd-amd64" ;;
+ i*86) platform="openbsd${version}" ;;
+ amd64) platform="openbsd${version}-amd64" ;;
*) platform="unknown" ;;
esac ;;
# cygwin
CYGWIN*)
case "$cpu" in
- i*86) platform="cygwin" ;;
- x86_64|ia64) platform="cygwin" ;;
+ i*86) platform="mswin" ;; # cygwin
+ # Pavneet Arora. 20190924. For Cygwin 64-bit platform should be win64.
+ x86_64|ia64) platform="win64" ;; # cygwin-64
*) platform="unknown" ;;
esac ;;
# UWIN
@@ -145,15 +147,18 @@ fi
rsync -rlptv rsync://contextgarden.net/minimals/setup/$platform/bin .
# use native windows binaries on cygwin
-if test "$platform" = "cygwin" ; then
- platform=mswin
-fi
+# Pavneet Arora. 20190924.
+# ..Commented out the following section entirely.
+#if test "$platform" = "cygwin" ; then
+# platform=mswin
+#fi
# download or update the distribution
# you may remove the --context=beta switch if you want to use "current"
# you can use --engine=luatex if you want just mkiv
env PATH="$PWD/bin:$CONTEXTROOT/texmf-$platform/bin:$PATH" MTX_PLATFORM="$platform" \
-./bin/mtxrun --script ./bin/mtx-update.lua --force --update --make --context=beta --platform="$platform" --texroot="$CONTEXTROOT" $@
+./bin/mtxrun --script ./bin/mtx-update.lua --force --update --make --context=beta --engine=luatex --platform="$platform" --texroot="$CONTEXTROOT" $@
+echo "./bin/mtxrun --script ./bin/mtx-update.lua --force --update --make --context=beta --engine=luatex --platform=\"$platform\" --texroot=\"$CONTEXTROOT\" $@"
echo
echo "When you want to use context, you need to initialize the tree by typing:"
diff --git a/scripts/context/stubs/mswin/mtxrun.lua b/scripts/context/stubs/mswin/mtxrun.lua
index 997fbcaec..1f7be7b72 100644
--- a/scripts/context/stubs/mswin/mtxrun.lua
+++ b/scripts/context/stubs/mswin/mtxrun.lua
@@ -8407,7 +8407,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["util-sac"] = package.loaded["util-sac"] or true
--- original size: 11332, stripped down to: 8420
+-- original size: 12898, stripped down to: 9275
if not modules then modules={} end modules ['util-sac']={
version=1.001,
@@ -8442,6 +8442,7 @@ end
function streams.size(f)
return f and f[3] or 0
end
+streams.getsize=streams.size
function streams.setposition(f,i)
if f[4] then
if i<=0 then
@@ -8826,6 +8827,50 @@ else
return t
end
end
+do
+ local files=utilities.files
+ if files then
+ local openfile=files.open
+ local openstream=streams.open
+ local openstring=streams.openstring
+ local setmetatable=setmetatable
+ function io.newreader(str,method)
+ local f,m
+ if method=="string" then
+ f=openstring(str)
+ m=streams
+ elseif method=="stream" then
+ f=openstream(str)
+ m=streams
+ else
+ f=openfile(str,"rb")
+ m=files
+ end
+ if f then
+ local t={}
+ setmetatable(t,{
+ __index=function(t,k)
+ local r=m[k]
+ if k=="close" then
+ if f then
+ m.close(f)
+ f=nil
+ end
+ return function() end
+ elseif r then
+ local v=function(_,a,b) return r(f,a,b) end
+ t[k]=v
+ return v
+ else
+ print("unknown key",k)
+ end
+ end
+ } )
+ return t
+ end
+ end
+ end
+end
end -- of closure
@@ -14119,7 +14164,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["util-lua"] = package.loaded["util-lua"] or true
--- original size: 6778, stripped down to: 4699
+-- original size: 7149, stripped down to: 4997
if not modules then modules={} end modules ['util-lua']={
version=1.001,
@@ -14144,6 +14189,11 @@ luautilities.nofstrippedchunks=0
luautilities.nofstrippedbytes=0
local strippedchunks={}
luautilities.strippedchunks=strippedchunks
+if not LUATEXENGINE then
+ LUATEXENGINE=status.luatex_engine and string.lower(status.luatex_engine)
+ JITSUPPORTED=LUATEXENGINE=="luajittex" or jit
+ CONTEXTLMTXMODE=CONTEXTLMTXMODE or (LUATEXENGINE=="luametatex" and 1) or 0
+end
luautilities.suffixes={
tma="tma",
tmc=(CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 and "tmd") or (jit and "tmb") or "tmc",
@@ -14153,7 +14203,7 @@ luautilities.suffixes={
luv="luv",
luj="luj",
tua="tua",
- tuc="tuc",
+ tuc=(CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 and "tud") or (jit and "tub") or "tuc",
}
local function register(name)
if tracestripping then
@@ -25733,8 +25783,8 @@ end -- of closure
-- used libraries : l-bit32.lua l-lua.lua l-macro.lua l-sandbox.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-sha.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua util-soc-imp-reset.lua util-soc-imp-socket.lua util-soc-imp-copas.lua util-soc-imp-ltn12.lua util-soc-imp-mime.lua util-soc-imp-url.lua util-soc-imp-headers.lua util-soc-imp-tp.lua util-soc-imp-http.lua util-soc-imp-ftp.lua util-soc-imp-smtp.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-tpl.lua util-sbx.lua util-mrg.lua util-env.lua luat-env.lua util-zip.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua
-- skipped libraries : -
--- original bytes : 1027480
--- stripped bytes : 407301
+-- original bytes : 1029417
+-- stripped bytes : 408085
-- end library merge
diff --git a/scripts/context/stubs/unix/mtxrun b/scripts/context/stubs/unix/mtxrun
index 997fbcaec..1f7be7b72 100644
--- a/scripts/context/stubs/unix/mtxrun
+++ b/scripts/context/stubs/unix/mtxrun
@@ -8407,7 +8407,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["util-sac"] = package.loaded["util-sac"] or true
--- original size: 11332, stripped down to: 8420
+-- original size: 12898, stripped down to: 9275
if not modules then modules={} end modules ['util-sac']={
version=1.001,
@@ -8442,6 +8442,7 @@ end
function streams.size(f)
return f and f[3] or 0
end
+streams.getsize=streams.size
function streams.setposition(f,i)
if f[4] then
if i<=0 then
@@ -8826,6 +8827,50 @@ else
return t
end
end
+do
+ local files=utilities.files
+ if files then
+ local openfile=files.open
+ local openstream=streams.open
+ local openstring=streams.openstring
+ local setmetatable=setmetatable
+ function io.newreader(str,method)
+ local f,m
+ if method=="string" then
+ f=openstring(str)
+ m=streams
+ elseif method=="stream" then
+ f=openstream(str)
+ m=streams
+ else
+ f=openfile(str,"rb")
+ m=files
+ end
+ if f then
+ local t={}
+ setmetatable(t,{
+ __index=function(t,k)
+ local r=m[k]
+ if k=="close" then
+ if f then
+ m.close(f)
+ f=nil
+ end
+ return function() end
+ elseif r then
+ local v=function(_,a,b) return r(f,a,b) end
+ t[k]=v
+ return v
+ else
+ print("unknown key",k)
+ end
+ end
+ } )
+ return t
+ end
+ end
+ end
+end
end -- of closure
@@ -14119,7 +14164,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["util-lua"] = package.loaded["util-lua"] or true
--- original size: 6778, stripped down to: 4699
+-- original size: 7149, stripped down to: 4997
if not modules then modules={} end modules ['util-lua']={
version=1.001,
@@ -14144,6 +14189,11 @@ luautilities.nofstrippedchunks=0
luautilities.nofstrippedbytes=0
local strippedchunks={}
luautilities.strippedchunks=strippedchunks
+if not LUATEXENGINE then
+ LUATEXENGINE=status.luatex_engine and string.lower(status.luatex_engine)
+ JITSUPPORTED=LUATEXENGINE=="luajittex" or jit
+ CONTEXTLMTXMODE=CONTEXTLMTXMODE or (LUATEXENGINE=="luametatex" and 1) or 0
+end
luautilities.suffixes={
tma="tma",
tmc=(CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 and "tmd") or (jit and "tmb") or "tmc",
@@ -14153,7 +14203,7 @@ luautilities.suffixes={
luv="luv",
luj="luj",
tua="tua",
- tuc="tuc",
+ tuc=(CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 and "tud") or (jit and "tub") or "tuc",
}
local function register(name)
if tracestripping then
@@ -25733,8 +25783,8 @@ end -- of closure
-- used libraries : l-bit32.lua l-lua.lua l-macro.lua l-sandbox.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-sha.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua util-soc-imp-reset.lua util-soc-imp-socket.lua util-soc-imp-copas.lua util-soc-imp-ltn12.lua util-soc-imp-mime.lua util-soc-imp-url.lua util-soc-imp-headers.lua util-soc-imp-tp.lua util-soc-imp-http.lua util-soc-imp-ftp.lua util-soc-imp-smtp.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-tpl.lua util-sbx.lua util-mrg.lua util-env.lua luat-env.lua util-zip.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua
-- skipped libraries : -
--- original bytes : 1027480
--- stripped bytes : 407301
+-- original bytes : 1029417
+-- stripped bytes : 408085
-- end library merge
diff --git a/scripts/context/stubs/win64/mtxrun.lua b/scripts/context/stubs/win64/mtxrun.lua
index 997fbcaec..1f7be7b72 100644
--- a/scripts/context/stubs/win64/mtxrun.lua
+++ b/scripts/context/stubs/win64/mtxrun.lua
@@ -8407,7 +8407,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["util-sac"] = package.loaded["util-sac"] or true
--- original size: 11332, stripped down to: 8420
+-- original size: 12898, stripped down to: 9275
if not modules then modules={} end modules ['util-sac']={
version=1.001,
@@ -8442,6 +8442,7 @@ end
function streams.size(f)
return f and f[3] or 0
end
+streams.getsize=streams.size
function streams.setposition(f,i)
if f[4] then
if i<=0 then
@@ -8826,6 +8827,50 @@ else
return t
end
end
+do
+ local files=utilities.files
+ if files then
+ local openfile=files.open
+ local openstream=streams.open
+ local openstring=streams.openstring
+ local setmetatable=setmetatable
+ function io.newreader(str,method)
+ local f,m
+ if method=="string" then
+ f=openstring(str)
+ m=streams
+ elseif method=="stream" then
+ f=openstream(str)
+ m=streams
+ else
+ f=openfile(str,"rb")
+ m=files
+ end
+ if f then
+ local t={}
+ setmetatable(t,{
+ __index=function(t,k)
+ local r=m[k]
+ if k=="close" then
+ if f then
+ m.close(f)
+ f=nil
+ end
+ return function() end
+ elseif r then
+ local v=function(_,a,b) return r(f,a,b) end
+ t[k]=v
+ return v
+ else
+ print("unknown key",k)
+ end
+ end
+ } )
+ return t
+ end
+ end
+ end
+end
end -- of closure
@@ -14119,7 +14164,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["util-lua"] = package.loaded["util-lua"] or true
--- original size: 6778, stripped down to: 4699
+-- original size: 7149, stripped down to: 4997
if not modules then modules={} end modules ['util-lua']={
version=1.001,
@@ -14144,6 +14189,11 @@ luautilities.nofstrippedchunks=0
luautilities.nofstrippedbytes=0
local strippedchunks={}
luautilities.strippedchunks=strippedchunks
+if not LUATEXENGINE then
+ LUATEXENGINE=status.luatex_engine and string.lower(status.luatex_engine)
+ JITSUPPORTED=LUATEXENGINE=="luajittex" or jit
+ CONTEXTLMTXMODE=CONTEXTLMTXMODE or (LUATEXENGINE=="luametatex" and 1) or 0
+end
luautilities.suffixes={
tma="tma",
tmc=(CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 and "tmd") or (jit and "tmb") or "tmc",
@@ -14153,7 +14203,7 @@ luautilities.suffixes={
luv="luv",
luj="luj",
tua="tua",
- tuc="tuc",
+ tuc=(CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 and "tud") or (jit and "tub") or "tuc",
}
local function register(name)
if tracestripping then
@@ -25733,8 +25783,8 @@ end -- of closure
-- used libraries : l-bit32.lua l-lua.lua l-macro.lua l-sandbox.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-sha.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua util-soc-imp-reset.lua util-soc-imp-socket.lua util-soc-imp-copas.lua util-soc-imp-ltn12.lua util-soc-imp-mime.lua util-soc-imp-url.lua util-soc-imp-headers.lua util-soc-imp-tp.lua util-soc-imp-http.lua util-soc-imp-ftp.lua util-soc-imp-smtp.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-tpl.lua util-sbx.lua util-mrg.lua util-env.lua luat-env.lua util-zip.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua
-- skipped libraries : -
--- original bytes : 1027480
--- stripped bytes : 407301
+-- original bytes : 1029417
+-- stripped bytes : 408085
-- end library merge
diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii
index eced6e05b..a1f67933b 100644
--- a/tex/context/base/mkii/cont-new.mkii
+++ b/tex/context/base/mkii/cont-new.mkii
@@ -11,7 +11,7 @@
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.
-\newcontextversion{2019.09.10 20:03}
+\newcontextversion{2019.09.27 17:59}
%D This file is loaded at runtime, thereby providing an
%D excellent place for hacks, patches, extensions and new
diff --git a/tex/context/base/mkii/context.mkii b/tex/context/base/mkii/context.mkii
index b3afbbceb..2a4b1588d 100644
--- a/tex/context/base/mkii/context.mkii
+++ b/tex/context/base/mkii/context.mkii
@@ -20,7 +20,7 @@
%D your styles an modules.
\edef\contextformat {\jobname}
-\edef\contextversion{2019.09.10 20:03}
+\edef\contextversion{2019.09.27 17:59}
%D For those who want to use this:
diff --git a/tex/context/base/mkii/mult-en.mkii b/tex/context/base/mkii/mult-en.mkii
index 65721ddaa..77f45f832 100644
--- a/tex/context/base/mkii/mult-en.mkii
+++ b/tex/context/base/mkii/mult-en.mkii
@@ -234,6 +234,7 @@
\setinterfacevariable{hanging}{hanging}
\setinterfacevariable{head}{head}
\setinterfacevariable{header}{header}
+\setinterfacevariable{headintext}{headintext}
\setinterfacevariable{height}{height}
\setinterfacevariable{helptext}{helptext}
\setinterfacevariable{hencefore}{hencefore}
diff --git a/tex/context/base/mkii/mult-it.mkii b/tex/context/base/mkii/mult-it.mkii
index 432917617..a5d956ba4 100644
--- a/tex/context/base/mkii/mult-it.mkii
+++ b/tex/context/base/mkii/mult-it.mkii
@@ -234,6 +234,7 @@
\setinterfacevariable{hanging}{sospeso}
\setinterfacevariable{head}{testa}
\setinterfacevariable{header}{intestazione}
+\setinterfacevariable{headintext}{headintext}
\setinterfacevariable{height}{altezza}
\setinterfacevariable{helptext}{testoaiuto}
\setinterfacevariable{hencefore}{precedente}
@@ -867,6 +868,7 @@
\setinterfaceconstant{hfil}{hfil}
\setinterfaceconstant{hidenumber}{hidenumber}
\setinterfaceconstant{hoffset}{hoffset}
+\setinterfaceconstant{horizontal}{orizzontale}
\setinterfaceconstant{horoffset}{horoffset}
\setinterfaceconstant{hyphen}{hyphen}
\setinterfaceconstant{hyphens}{hyphens}
@@ -967,9 +969,12 @@
\setinterfaceconstant{menu}{menu}
\setinterfaceconstant{method}{metodo}
\setinterfaceconstant{middle}{centro}
+\setinterfaceconstant{middlecolor}{middlecolor}
\setinterfaceconstant{middlecommand}{middlecommand}
\setinterfaceconstant{middlespeech}{middlespeech}
+\setinterfaceconstant{middlestyle}{middlestyle}
\setinterfaceconstant{middletext}{testocentro}
+\setinterfaceconstant{middlewidth}{middlewidth}
\setinterfaceconstant{midsentence}{midsentence}
\setinterfaceconstant{min}{min}
\setinterfaceconstant{mindepth}{mindepth}
@@ -1287,6 +1292,7 @@
\setinterfaceconstant{vcompact}{vcompact}
\setinterfaceconstant{vector}{vector}
\setinterfaceconstant{veroffset}{veroffset}
+\setinterfaceconstant{vertical}{verticale}
\setinterfaceconstant{vfactor}{vfactor}
\setinterfaceconstant{vfil}{vfil}
\setinterfaceconstant{viewerprefix}{viewerprefix}
@@ -1297,6 +1303,7 @@
\setinterfaceconstant{white}{bianco}
\setinterfaceconstant{width}{ampiezza}
\setinterfaceconstant{words}{words}
+\setinterfaceconstant{xanchor}{xanchor}
\setinterfaceconstant{xfactor}{xfactor}
\setinterfaceconstant{xhtml}{xhtml}
\setinterfaceconstant{xmax}{xmax}
@@ -1304,6 +1311,7 @@
\setinterfaceconstant{xoffset}{xoffset}
\setinterfaceconstant{xscale}{xscale}
\setinterfaceconstant{xstep}{xstep}
+\setinterfaceconstant{yanchor}{yanchor}
\setinterfaceconstant{yfactor}{yfactor}
\setinterfaceconstant{ymax}{ymax}
\setinterfaceconstant{yoffset}{yoffset}
diff --git a/tex/context/base/mkii/mult-ro.mkii b/tex/context/base/mkii/mult-ro.mkii
index 248cf8b18..ccdce5dcc 100644
--- a/tex/context/base/mkii/mult-ro.mkii
+++ b/tex/context/base/mkii/mult-ro.mkii
@@ -234,6 +234,7 @@
\setinterfacevariable{hanging}{suspendat}
\setinterfacevariable{head}{antet}
\setinterfacevariable{header}{antet}
+\setinterfacevariable{headintext}{headintext}
\setinterfacevariable{height}{inaltime}
\setinterfacevariable{helptext}{textajutator}
\setinterfacevariable{hencefore}{precedent}
diff --git a/tex/context/base/mkiv/back-ini.lua b/tex/context/base/mkiv/back-ini.lua
index bc4a67bc0..b0af61edc 100644
--- a/tex/context/base/mkiv/back-ini.lua
+++ b/tex/context/base/mkiv/back-ini.lua
@@ -182,6 +182,29 @@ implement {
end
}
+
+local page_x_origin = 0
+local page_y_origin = 0
+
+function codeinjections.setpageorigin(x,y)
+ page_x_origin = x
+ page_y_origin = y
+end
+
+function codeinjections.getpageorigin()
+ local x = page_x_origin
+ local y = page_y_origin
+ page_x_origin = 0
+ page_y_origin = 0
+ return x, y, (x ~= 0 or y ~= 0)
+end
+
+implement {
+ name = "setpageorigin",
+ arguments = { "dimension", "dimension" },
+ actions = codeinjections.setpageorigin,
+}
+
-- could also be codeinjections
function backends.noflatelua()
diff --git a/tex/context/base/mkiv/back-pdf.mkiv b/tex/context/base/mkiv/back-pdf.mkiv
index ff05e647f..ec1c641e6 100644
--- a/tex/context/base/mkiv/back-pdf.mkiv
+++ b/tex/context/base/mkiv/back-pdf.mkiv
@@ -145,8 +145,10 @@
%D Bah, this is also needed for tikz:
-\let\pdfsavepos \savepos
-\let\pdflastxpos\lastxpos
-\let\pdflastypos\lastypos
+\ifdefined\pdfsavepos\else
+ \let\pdfsavepos \savepos
+ \let\pdflastxpos\lastxpos
+ \let\pdflastypos\lastypos
+\fi
\protect \endinput
diff --git a/tex/context/base/mkiv/back-pdf.mkxl b/tex/context/base/mkiv/back-pdf.mkxl
index 2b0c4d0b7..ab65458c5 100644
--- a/tex/context/base/mkiv/back-pdf.mkxl
+++ b/tex/context/base/mkiv/back-pdf.mkxl
@@ -145,4 +145,12 @@
\let\pdfactualtext\pdfbackendactualtext
+%D Bah, this is also needed for tikz:
+
+\ifdefined\pdfsavepos\else
+ \let\pdfsavepos \savepos
+ \let\pdflastxpos\lastxpos
+ \let\pdflastypos\lastypos
+\fi
+
\protect \endinput
diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv
index 0a68310a0..6b05f4174 100644
--- a/tex/context/base/mkiv/cont-new.mkiv
+++ b/tex/context/base/mkiv/cont-new.mkiv
@@ -13,7 +13,7 @@
% \normalend % uncomment this to get the real base runtime
-\newcontextversion{2019.09.10 20:03}
+\newcontextversion{2019.09.27 17:59}
%D This file is loaded at runtime, thereby providing an excellent place for
%D hacks, patches, extensions and new features.
diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv
index 2c17d4b91..907985beb 100644
--- a/tex/context/base/mkiv/context.mkiv
+++ b/tex/context/base/mkiv/context.mkiv
@@ -45,7 +45,7 @@
%D {YYYY.MM.DD HH:MM} format.
\edef\contextformat {\jobname}
-\edef\contextversion{2019.09.10 20:03}
+\edef\contextversion{2019.09.27 17:59}
\edef\contextkind {beta}
%D Kind of special:
diff --git a/tex/context/base/mkiv/context.mkxl b/tex/context/base/mkiv/context.mkxl
index 39f647cc2..6651995c2 100644
--- a/tex/context/base/mkiv/context.mkxl
+++ b/tex/context/base/mkiv/context.mkxl
@@ -29,7 +29,7 @@
%D {YYYY.MM.DD HH:MM} format.
\edef\contextformat {\jobname}
-\edef\contextversion{2019.09.10 20:03}
+\edef\contextversion{2019.09.27 17:59}
\edef\contextkind {beta}
%D Kind of special:
diff --git a/tex/context/base/mkiv/driv-shp.lua b/tex/context/base/mkiv/driv-shp.lua
index ae99d84ce..8fe2b8483 100644
--- a/tex/context/base/mkiv/driv-shp.lua
+++ b/tex/context/base/mkiv/driv-shp.lua
@@ -150,6 +150,7 @@ local updatefontstate
local pushorientation
local poporientation
local flushcharacter
+local flushfontchar
local flushrule
local flushliteral
local flushsetmatrix
@@ -241,6 +242,32 @@ local function flush_vf_packet(pos_h,pos_v,pos_r,font,char,data,factor,vfcommand
pos_h = pos_h + flushchar(font,char,font,chr,f,e)
end
end
+ elseif command == "use" then
+ local index = packet[2]
+ if index then
+ local fnt
+ if index == 0 then
+ fnt = font
+ else
+ local okay = fonts and fonts[index]
+ if okay then
+ fnt = okay.id
+ end
+ end
+ if fnt then
+ -- not efficient but ok for now as experiment
+ local d = characters[fnt]
+ if d then
+ for i=3,#packet do
+ local chr = packet[i]
+ local dat = d[chr]
+ if dat then
+ flushfontchar(fnt,chr,dat)
+ end
+ end
+ end
+ end
+ end
elseif command == "right" then
local h = packet[2] -- * siz
if factor ~= 0 and h ~= 0 then
@@ -1118,6 +1145,7 @@ function drivers.converters.lmtx(driver,box,smode,objnum,specification)
poporientation = flushers.poporientation
flushcharacter = flushers.character
+ flushfontchar = flushers.fontchar
flushrule = flushers.rule
flushsimplerule = flushers.simplerule
flushspecial = flushers.special
diff --git a/tex/context/base/mkiv/font-con.lua b/tex/context/base/mkiv/font-con.lua
index 9c1e825fa..652794142 100644
--- a/tex/context/base/mkiv/font-con.lua
+++ b/tex/context/base/mkiv/font-con.lua
@@ -444,9 +444,10 @@ function constructors.scale(tfmdata,specification)
-- boundarychar = 65536, -- there is now a string 'right_boundary'
-- false_boundarychar = 65536, -- produces invalid tfm in luatex
--
- targetproperties.language = properties.language or "dflt" -- inherited
- targetproperties.script = properties.script or "dflt" -- inherited
- targetproperties.mode = properties.mode or "base" -- inherited
+ targetproperties.language = properties.language or "dflt" -- inherited
+ targetproperties.script = properties.script or "dflt" -- inherited
+ targetproperties.mode = properties.mode or "base" -- inherited
+ targetproperties.method = properties.method
--
local askedscaledpoints = scaledpoints
local scaledpoints, delta = constructors.calculatescale(tfmdata,scaledpoints,nil,specification) -- no shortcut, dan be redefined
diff --git a/tex/context/base/mkiv/font-lib.mkvi b/tex/context/base/mkiv/font-lib.mkvi
index 081ae0f38..c9050f61b 100644
--- a/tex/context/base/mkiv/font-lib.mkvi
+++ b/tex/context/base/mkiv/font-lib.mkvi
@@ -28,7 +28,6 @@
% helpers
-
\registerctxluafile{font-otr}{optimize} % opentype fontloader
\registerctxluafile{font-web}{} % opentype fontloader
\registerctxluafile{font-cff}{optimize} % quadratic outlines
@@ -51,7 +50,12 @@
\registerctxluafile{font-oth}{}
\registerctxluafile{font-osd}{}
% \doifelsefileexists{font-osm.lua}{\registerctxluafile{font-osm}{}}{}
-\registerctxluafile{font-ocl}{}
+
+\ifcase\contextlmtxmode
+ \registerctxluafile{font-ocl}{}
+\else
+ \registerctxluafile{font-ogr}{}
+\fi
% we use otf code for type one
@@ -61,12 +65,8 @@
% tfm
-\doifelsefileexists {font-tpk.lua} {
- \registerctxluafile{font-tpk}{optimize}
- \registerctxluafile{font-tfm}{}
-} {
- \registerctxluafile{font-tfm}{}
-}
+\registerctxluafile{font-tpk}{optimize}
+\registerctxluafile{font-tfm}{}
% name database
diff --git a/tex/context/base/mkiv/font-ocl.lua b/tex/context/base/mkiv/font-ocl.lua
index 10d2df270..9ce6982a8 100644
--- a/tex/context/base/mkiv/font-ocl.lua
+++ b/tex/context/base/mkiv/font-ocl.lua
@@ -8,6 +8,10 @@ if not modules then modules = { } end modules ['font-ocl'] = {
-- todo : user list of colors
+if CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 then
+ return
+end
+
local tostring, tonumber, next = tostring, tonumber, next
local round, max = math.round, math.round
local gsub, find = string.gsub, string.find
@@ -17,8 +21,6 @@ local setmetatableindex = table.setmetatableindex
local formatters = string.formatters
local tounicode = fonts.mappings.tounicode
-local bpfactor = number.dimenfactors.bp
-
local helpers = fonts.helpers
local charcommand = helpers.commands.char
@@ -367,19 +369,25 @@ local function pdftovirtual(tfmdata,pdfshapes,kind) -- kind = png|svg
for unicode, character in sortedhash(characters) do -- sort is nicer for svg
local index = character.index
if index then
- local pdf = pdfshapes[index]
- local typ = type(pdf)
- local data = nil
- local dx = nil
- local dy = nil
+ local pdf = pdfshapes[index]
+ local typ = type(pdf)
+ local data = nil
+ local dx = nil
+ local dy = nil
+ local scale = 1
if typ == "table" then
- data = pdf.data
- dx = pdf.x or pdf.dx or 0
- dy = pdf.y or pdf.dy or 0
+ data = pdf.data
+ dx = pdf.x or pdf.dx or 0
+ dy = pdf.y or pdf.dy or 0
+ scale = pdf.scale or 1
elseif typ == "string" then
data = pdf
dx = 0
dy = 0
+ elseif typ == "number" then
+ data = pdf
+ dx = 0
+ dy = 0
end
if data then
-- We can delay storage by a lua function in commands: but then we need to
@@ -402,7 +410,7 @@ local function pdftovirtual(tfmdata,pdfshapes,kind) -- kind = png|svg
-- mkiv
downcommand [dp + dy * hfactor],
rightcommand[ dx * hfactor],
- vfimage(wd,ht,dp,data,name),
+ vfimage(scale*wd,ht,dp,data,pdfshapes.filename or ""),
actuale,
}
character[kind] = true
@@ -457,12 +465,17 @@ do
-- poor mans variant for generic:
--
runner = function()
- return io.open("inkscape --export-area-drawing --shell > temp-otf-svg-shape.log","w")
+ return io.popen("inkscape --export-area-drawing --shell > temp-otf-svg-shape.log","w")
end
end
-- There are svg out there with bad viewBox specifications where shapes lay outside that area,
- -- but trying to correct that didin'w work out well enough so I discarded that code.
+ -- but trying to correct that didn't work out well enough so I discarded that code. BTW, we
+ -- decouple the inskape run and the loading run because inkscape is working in the background
+ -- in the files so we need to have unique files.
+ --
+ -- Because a generic setup can be flawed we need to catch bad inkscape runs which add a bit of
+ -- ugly overhead. Bah.
function otfsvg.topdf(svgshapes,tfmdata)
local pdfshapes = { }
@@ -476,6 +489,7 @@ do
local f_convert = formatters["%s --export-pdf=%s\n"]
local filterglyph = otfsvg.filterglyph
local nofdone = 0
+ local processed = { }
report_svg("processing %i svg containers",nofshapes)
statistics.starttiming()
for i=1,nofshapes do
@@ -487,30 +501,55 @@ do
local pdffile = f_pdffile(index)
savedata(svgfile,data)
inkscape:write(f_convert(svgfile,pdffile))
- pdfshapes[index] = true
+ processed[index] = true
nofdone = nofdone + 1
- if nofdone % 100 == 0 then
- report_svg("%i shapes processed",nofdone)
+ if nofdone % 25 == 0 then
+ report_svg("%i shapes submitted",nofdone)
end
end
end
end
+ if nofdone % 25 ~= 0 then
+ report_svg("%i shapes submitted",nofdone)
+ end
+ report_svg("processing can be going on for a while")
inkscape:write("quit\n")
inkscape:close()
report_svg("processing %i pdf results",nofshapes)
- for index in next, pdfshapes do
+ for index in next, processed do
local svgfile = f_svgfile(index)
local pdffile = f_pdffile(index)
-- local fntdata = descriptions[indices[index]]
-- local bounds = fntdata and fntdata.boundingbox
- pdfshapes[index] = {
- data = loaddata(pdffile),
- -- x = bounds and bounds[1] or 0,
- -- y = bounds and bounds[2] or 0,
- }
+ local pdfdata = loaddata(pdffile)
+ if pdfdata and pdfdata ~= "" then
+ pdfshapes[index] = {
+ data = pdfdata,
+ -- x = bounds and bounds[1] or 0,
+ -- y = bounds and bounds[2] or 0,
+ }
+ end
remove(svgfile)
remove(pdffile)
end
+local characters = tfmdata.characters
+for k, v in next, characters do
+ local d = descriptions[k]
+ local i = d.index
+ if i then
+ local p = pdfshapes[i]
+ if p then
+ local w = d.width
+ local l = d.boundingbox[1]
+ local r = d.boundingbox[3]
+ p.scale = (r - l) / w
+ p.x = l
+ end
+ end
+end
+ if not next(pdfshapes) then
+ report_svg("there are no converted shapes, fix your setup")
+ end
statistics.stoptiming()
if statistics.elapsedseconds then
report_svg("svg conversion time %s",statistics.elapsedseconds() or "-")
@@ -531,10 +570,12 @@ local function initializesvg(tfmdata,kind,value) -- hm, always value
end
local pdffile = containers.read(otf.pdfcache,hash)
local pdfshapes = pdffile and pdffile.pdfshapes
- if not pdfshapes or pdffile.timestamp ~= timestamp then
+ if not pdfshapes or pdffile.timestamp ~= timestamp or not next(pdfshapes) then
+ -- the next test tries to catch errors in generic usage but of course can result
+ -- in running again and again
local svgfile = containers.read(otf.svgcache,hash)
local svgshapes = svgfile and svgfile.svgshapes
- pdfshapes = svgshapes and otfsvg.topdf(svgshapes,tfmdata) or { }
+ pdfshapes = svgshapes and otfsvg.topdf(svgshapes,tfmdata,otf.pdfcache.writable,hash) or { }
containers.write(otf.pdfcache, hash, {
pdfshapes = pdfshapes,
timestamp = timestamp,
diff --git a/tex/context/base/mkiv/font-ogr.lua b/tex/context/base/mkiv/font-ogr.lua
new file mode 100644
index 000000000..c441ddbf7
--- /dev/null
+++ b/tex/context/base/mkiv/font-ogr.lua
@@ -0,0 +1,381 @@
+if not modules then modules = { } end modules ['font-ogr'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Here we deal with graphic variants and for now also color support ends up here
+-- but that might change. It's lmtx only code.
+
+if not context then
+ return
+elseif CONTEXTLMTXMODE == 0 then
+ return
+end
+
+local tostring, tonumber, next = tostring, tonumber, next
+local round, max, mod, div = math.round, math.round, math.mod, math.div
+local concat, setmetatableindex = table.concat, table.setmetatableindex
+local formatters = string.formatters
+
+local otf = fonts.handlers.otf
+local otfregister = otf.features.register
+otf.svgenabled = true
+otf.pngenabled = true
+
+-- Just to remind me ... rewritten around the time this was posted on YT which
+-- was also around the 2019 ctx meeting:
+--
+-- Gavin Harrison - "Threatening War" by The Pineapple Thief
+-- https://www.youtube.com/watch?v=ENF9wth4kwM
+
+-- todo: svg color plugin
+-- todo: get rid of double cm in svg (tricky as also elsewhere)
+-- todo: png offsets (depth)
+-- todo: maybe collapse indices so that we have less files (harder to debug)
+-- todo: manage (read: assign) font id's in lua so we know in advance
+
+do
+
+ -- This is a prelude to something better but I'm still experimenting.
+
+ local dropins = { }
+ fonts.dropins = dropins
+ local droppedin = 0
+ local identifiers = fonts.hashes.identifiers
+
+ function dropins.nextid()
+ droppedin = droppedin - 1
+ return droppedin
+ end
+
+ function dropins.provide(method,clone,t_tfmdata,indexdata,...)
+ droppedin = dropins.nextid()
+ local t_characters = t_tfmdata.characters
+ local t_descriptions = t_tfmdata.descriptions
+ local t_properties = t_tfmdata.properties
+ local d_tfmdata = setmetatableindex({ },t_tfmdata)
+ local d_properties = setmetatableindex({ },t_properties)
+ d_tfmdata.properties = d_properties
+ if clone then
+ local d_characters = setmetatableindex({ },t_characters)
+ local d_descriptions = setmetatableindex({ },t_descriptions)
+ d_tfmdata.characters = d_characters
+ d_tfmdata.descriptions = d_descriptions
+ end
+ d_properties.instance = - droppedin -- will become an extra element in the hash
+ t_properties.virtualized = true
+ identifiers[droppedin] = d_tfmdata
+ local fonts = t_tfmdata.fonts or { }
+ t_tfmdata.fonts = fonts
+ d_properties.format = "type3"
+ d_properties.method = method
+ d_properties.indexdata = { indexdata, ... } -- can take quite some memory
+ local slot = #fonts + 1
+ fonts[slot] = { id = droppedin }
+ if not clone then
+ for k, v in next, t_characters do
+ local index = v.index
+ if index and indexdata[index] then
+ -- todo: use extender
+ v.commands = { { "slot", slot, k } }
+ end
+ end
+ else
+ -- make sure that the drop characters should get a copy with no commands
+ -- (false will do)
+ end
+ return slot, droppedin, d_tfmdata, d_properties
+ end
+
+end
+
+-- This sits here for historcal reasons so for now we keep it here.
+
+local startactualtext = nil
+local stopactualtext = nil
+
+function otf.getactualtext(s)
+ if not startactualtext then
+ startactualtext = backends.codeinjections.startunicodetoactualtextdirect
+ stopactualtext = backends.codeinjections.stopunicodetoactualtextdirect
+ end
+ return startactualtext(s), stopactualtext()
+end
+
+-- This is also somewhat specific.
+
+local sharedpalettes do
+
+ sharedpalettes = { }
+
+ local colors = attributes.list[attributes.private('color')] or { }
+ local transparencies = attributes.list[attributes.private('transparency')] or { }
+
+ function otf.registerpalette(name,values)
+ sharedpalettes[name] = values
+ local color = lpdf.color
+ local transparency = lpdf.transparency
+ local register = colors.register
+ for i=1,#values do
+ local v = values[i]
+ if v == "textcolor" then
+ values[i] = false
+ else
+ local c = nil
+ local t = nil
+ if type(v) == "table" then
+ c = register(name,"rgb",
+ max(round((v.r or 0)*255),255)/255,
+ max(round((v.g or 0)*255),255)/255,
+ max(round((v.b or 0)*255),255)/255
+ )
+ else
+ c = colors[v]
+ t = transparencies[v]
+ end
+ if c and t then
+ values[i] = color(1,c) .. " " .. transparency(t)
+ elseif c then
+ values[i] = color(1,c)
+ elseif t then
+ values[i] = color(1,t)
+ end
+ end
+ end
+ end
+
+end
+
+do
+
+ local f_color = formatters["%.3f %.3f %.3f rg"]
+ local f_gray = formatters["%.3f g"]
+
+ local hash = setmetatableindex(function(t,k)
+ local v = k
+ t[k] = v
+ return v
+ end)
+
+ local function convert(t,k)
+ local v = { }
+ for i=1,#k do
+ local p = k[i]
+ local r, g, b = p[1], p[2], p[3]
+ if r == g and g == b then
+ v[i] = hash[f_gray(r/255)]
+ else
+ v[i] = hash[f_color(r/255,g/255,b/255)]
+ end
+ end
+ t[k] = v
+ return v
+ end
+
+ local function initialize(tfmdata,kind,value) -- we really need the id ... todo
+ if value then
+ local resources = tfmdata.resources
+ local palettes = resources.colorpalettes
+ if palettes then
+ --
+ local converted = resources.converted
+ if not converted then
+ converted = setmetatableindex(convert)
+ resources.converted = converted
+ end
+ local colorvalues = sharedpalettes[value]
+ local default = false -- so the text color (bad for icon overloads)
+ if colorvalues then
+ default = colorvalues[#colorvalues]
+ else
+ colorvalues = converted[palettes[tonumber(value) or 1] or palettes[1]] or { }
+ end
+ local classes = #colorvalues
+ if classes == 0 then
+ return
+ end
+ --
+ -- todo: delay with lua function or plugin in vf handler, saves a lot of
+ -- overhead
+ --
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local droppedin, tfmdrop, dropchars, dropdescs, colrshapes
+ -- alternative 1:
+ -- local slots = { }
+ -- alternative 2:
+ local idx = 255
+ local slot = 0
+ --
+ for k, v in next, characters do
+ local index = v.index
+ if index then
+ local description = descriptions[k]
+ if description then
+ local colorlist = description.colors
+ if colorlist then
+ -- alternative 1:
+ -- local fnt = div(index,256)
+ -- local idx = mod(index,256)
+ -- local slt = slots[fnt]
+ -- if not slt then
+ -- colrshapes = { }
+ -- slot, droppedin, tfmdrop = fonts.dropins.provide("color",true,tfmdata,colrshapes,colorvalues)
+ -- slt = { slot, colrshapes, tfmdrop.characters, tfmdrop.descriptions }
+ -- slots[fnt] = slt
+ -- end
+ -- slot = slt[1]
+ -- colrshapes = slt[2]
+ -- dropchars = slt[3]
+ -- dropdescs = slt[4]
+ -- alternative 2:
+ if idx >= 255 then
+ idx = 1
+ colrshapes = { }
+ slot, droppedin, tfmdrop = fonts.dropins.provide("color",true,tfmdata,colrshapes,colorvalues)
+ dropchars = tfmdrop.characters
+ dropdescs = tfmdrop.descriptions
+ else
+ idx = idx + 1
+ end
+ --
+ colrshapes[idx] = description
+ -- todo: use extender
+ local u = { "use", 0 }
+ for i=1,#colorlist do
+ u[i+2] = colorlist[i].slot
+ end
+ v.commands = { u, { "slot", slot, idx } }
+ -- hack to prevent that type 3 also gets 'use' flags .. todo
+ local c = { commands = false, index = idx, dropin = tfmdata }
+ local d = { index = idx, dropin = tfmdata }
+ setmetatableindex(c,v)
+ setmetatableindex(d,description)
+ dropchars[idx] = c
+ dropdescs[idx] = d
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ fonts.handlers.otf.features.register {
+ name = "colr",
+ description = "color glyphs",
+ manipulators = {
+ base = initialize,
+ node = initialize,
+ }
+ }
+
+end
+
+do
+
+ local report_svg = logs.reporter("fonts","svg")
+
+ local cached = true directives.register("fonts.svg.cached", function(v) cached = v end)
+
+ local function initializesvg(tfmdata,kind,value) -- hm, always value
+ if value then
+ local properties = tfmdata.properties
+ local svg = properties.svg
+ local hash = svg and svg.hash
+ local timestamp = svg and svg.timestamp
+ if not hash then
+ return
+ end
+ if cached then
+ -- we need a different hash than for mkiv, so we append:
+ local pdfhash = hash .. "-svg"
+ local pdffile = containers.read(otf.pdfcache,pdfhash)
+ local pdfshapes = pdffile and pdffile.pdfshapes
+ local pdftarget = file.join(otf.pdfcache.writable,file.addsuffix(pdfhash,"pdf"))
+ if not pdfshapes or pdffile.timestamp ~= timestamp or not next(pdfshapes) or not lfs.isfile(pdftarget) then
+ local svgfile = containers.read(otf.svgcache,hash)
+ local svgshapes = svgfile and svgfile.svgshapes
+ pdfshapes = svgshapes and metapost.svgshapestopdf(svgshapes,pdftarget,report_svg) or { }
+ containers.write(otf.pdfcache, pdfhash, {
+ pdfshapes = pdfshapes,
+ timestamp = timestamp,
+ })
+ end
+ -- this can be delayed, no need to keep this in mem
+ fonts.dropins.provide("pdf",false,tfmdata,pdfshapes)
+ else
+ local mpsfile = containers.read(otf.mpscache,hash)
+ local mpsshapes = mpsfile and mpsfile.mpsshapes
+ if not mpsshapes or mpsfile.timestamp ~= timestamp or not next(mpsshapes) then
+ local svgfile = containers.read(otf.svgcache,hash)
+ local svgshapes = svgfile and svgfile.svgshapes
+ -- still suboptimal
+ mpsshapes = svgshapes and metapost.svgshapestomp(svgshapes,report_svg) or { }
+ containers.write(otf.mpscache, hash, {
+ mpsshapes = mpsshapes,
+ timestamp = timestamp,
+ })
+ end
+ fonts.dropins.provide("mps",false,tfmdata,mpsshapes)
+ end
+ end
+ end
+
+ otfregister {
+ name = "svg",
+ description = "svg glyphs",
+ manipulators = {
+ base = initializesvg,
+ node = initializesvg,
+ }
+ }
+
+end
+
+do
+
+ -- If this is really critical we can also use a pdf file as cache but I don't expect
+ -- png fonts to remain used.
+
+ local report_png = logs.reporter("fonts","png conversion")
+
+ local function initializepng(tfmdata,kind,value) -- hm, always value
+ if value then
+ local properties = tfmdata.properties
+ local png = properties.png
+ local hash = png and png.hash
+ local timestamp = png and png.timestamp
+ if not hash then
+ return
+ end
+ local pngfile = containers.read(otf.pngcache,hash)
+ local pngshapes = pngfile and pngfile.pngshapes
+ if pngshapes then
+ fonts.dropins.provide("png",false,tfmdata,pngshapes)
+ end
+ end
+ end
+
+ otfregister {
+ name = "sbix",
+ description = "sbix glyphs",
+ manipulators = {
+ base = initializepng,
+ node = initializepng,
+ }
+ }
+
+ otfregister {
+ name = "cblc",
+ description = "cblc glyphs",
+ manipulators = {
+ base = initializepng,
+ node = initializepng,
+ }
+ }
+
+end
diff --git a/tex/context/base/mkiv/font-otl.lua b/tex/context/base/mkiv/font-otl.lua
index f8cd8217c..8bf44d79e 100644
--- a/tex/context/base/mkiv/font-otl.lua
+++ b/tex/context/base/mkiv/font-otl.lua
@@ -57,6 +57,7 @@ otf.cache = containers.define("fonts", "otl", otf.version, true)
otf.svgcache = containers.define("fonts", "svg", otf.version, true)
otf.pngcache = containers.define("fonts", "png", otf.version, true)
otf.pdfcache = containers.define("fonts", "pdf", otf.version, true)
+otf.mpscache = containers.define("fonts", "mps", otf.version, true)
otf.svgenabled = false
otf.pngenabled = false
diff --git a/tex/context/base/mkiv/font-tpk.lua b/tex/context/base/mkiv/font-tpk.lua
index 2a0810a6b..26e3fc0f0 100644
--- a/tex/context/base/mkiv/font-tpk.lua
+++ b/tex/context/base/mkiv/font-tpk.lua
@@ -269,18 +269,18 @@ do
end
local template = formatters [ [[
- %.3N 0 %i %i %i %i d1
- q
- %i 0 0 %i %i %i cm
- BI
- /W %i
- /H %i
- /IM true
- /BPC 1
- /D [1 0]
- ID %t
- EI
- Q]] ]
+%.3N 0 %i %i %i %i d1
+q
+%i 0 0 %i %i %i cm
+BI
+ /W %i
+ /H %i
+ /IM true
+ /BPC 1
+ /D [1 0]
+ID %t
+EI
+Q]] ]
function readers.pktopdf(glyph,width)
local xsize = glyph.xsize or 0
@@ -319,9 +319,7 @@ do
r = r + 1 ; result[r] = char(extract(b,8,8))
end
end
- return
- template(width,llx,lly,urx,ury,xdpi,ydpi,llx,lly,xsize,ysize,result),
- llx, lly, urx, ury
+ return template(width,llx,lly,urx,ury,xdpi,ydpi,llx,lly,xsize,ysize,result)
end
function readers.loadpk(filename)
diff --git a/tex/context/base/mkiv/grph-chk.lua b/tex/context/base/mkiv/grph-chk.lua
index fedd8d3ca..f286dedd6 100644
--- a/tex/context/base/mkiv/grph-chk.lua
+++ b/tex/context/base/mkiv/grph-chk.lua
@@ -207,7 +207,7 @@ function checkers.png(data) -- same as jpg (for now)
local used = data.used
if request and used and not request.scanimage then
local identify = graphics.identify
- local inject = lpdf.injectors.png
+ local inject = lpdf.injectors.png -- currently pdf specific
local found = false
request.scanimage = function(t)
local result = wrappedidentify(identify,t.filename)
diff --git a/tex/context/base/mkiv/grph-img.lua b/tex/context/base/mkiv/grph-img.lua
index cde9c8442..795ea4827 100644
--- a/tex/context/base/mkiv/grph-img.lua
+++ b/tex/context/base/mkiv/grph-img.lua
@@ -29,6 +29,8 @@ local skipbytes = files.skip
local setposition = files.setposition
local getposition = files.getposition
+local newreader = io.newreader
+
local setmetatableindex = table.setmetatableindex
local setmetatablecall = table.setmetatablecall
@@ -543,8 +545,8 @@ do
if once then
for i=1,#t do
local l = t[i]
- setposition(f,l.offset)
- t[i] = readstring(f,l.length)
+ f:setposition(l.offset)
+ t[i] = f:readstring(l.length)
end
local data = concat(t)
-- t wiped in caller
@@ -553,14 +555,14 @@ do
local data = { }
for i=1,#t do
local l = t[i]
- setposition(f,l.offset)
- data[i] = readstring(f,l.length)
+ f:setposition(l.offset)
+ data[i] = f:readstring(l.length)
end
return concat(data)
end
end
- function identifiers.png(filename)
+ function identifiers.png(filename,method)
local specification = {
filename = filename,
filetype = "png",
@@ -569,7 +571,7 @@ do
specification.error = "invalid filename"
return specification -- error
end
- local f = io.open(filename,"rb")
+ local f = newreader(filename,method)
if not f then
specification.error = "unable to open file"
return specification -- error
@@ -581,44 +583,44 @@ do
specification.pagenum = 1
specification.offset = 0
specification.length = 0
- local filesize = getsize(f) -- seek end
+ local filesize = f:getsize() -- seek end
local tables = { }
- local banner = readstring(f,8)
+ local banner = f:readstring(8)
if banner ~= "\137PNG\013\010\026\010" then
specification.error = "no png file"
return specification -- error
end
while true do
- local position = getposition(f)
+ local position = f:getposition()
if position >= filesize then
break
end
- local length = readcardinal4(f)
+ local length = f:readcardinal4()
if not length then
break
end
- local kind = readstring(f,4)
+ local kind = f:readstring(4)
if kind then
kind = lower(kind)
else
break
end
if kind == "ihdr" then -- metadata
- specification.xsize = readcardinal4(f)
- specification.ysize = readcardinal4(f)
- specification.colordepth = readcardinal(f)
- specification.colorspace = readcardinal(f)
- specification.compression = readcardinal(f)
- specification.filter = readcardinal(f)
- specification.interlace = readcardinal(f)
+ specification.xsize = f:readcardinal4()
+ specification.ysize = f:readcardinal4()
+ specification.colordepth = f:readcardinal()
+ specification.colorspace = f:readcardinal()
+ specification.compression = f:readcardinal()
+ specification.filter = f:readcardinal()
+ specification.interlace = f:readcardinal()
tables[kind] = true
elseif kind == "iend" then
tables[kind] = true
break
elseif kind == "phys" then
- local x = readcardinal4(f)
- local y = readcardinal4(f)
- local u = readcardinal(f)
+ local x = f:readcardinal4()
+ local y = f:readcardinal4()
+ local u = f:readcardinal()
if u == 1 then -- meters
-- x = round(0.0254 * x)
-- y = round(0.0254 * y)
@@ -633,14 +635,16 @@ do
tables[kind] = t
end
t[#t+1] = {
- offset = getposition(f),
+-- offset = getposition(f),
+ offset = f:getposition(),
length = length,
}
else
tables[kind] = true
end
- setposition(f,position+length+12) -- #size #kind #crc
+ f:setposition(position+length+12) -- #size #kind #crc
end
+ f:close()
specification.tables = tables
return specification
end
diff --git a/tex/context/base/mkiv/lpdf-emb.lua b/tex/context/base/mkiv/lpdf-emb.lua
index dbf6fd4d3..1cdcac1ae 100644
--- a/tex/context/base/mkiv/lpdf-emb.lua
+++ b/tex/context/base/mkiv/lpdf-emb.lua
@@ -45,7 +45,9 @@ local report_fonts = logs.reporter("backend","fonts")
local trace_fonts = false
local trace_details = false
-local dimenfactors = number.dimenfactors
+local dimenfactors = number.dimenfactors
+local bpfactor = dimenfactors.bp
+local ptfactor = dimenfactors.pt
trackers.register("backend.pdf.fonts",function(v) trace_fonts = v end)
@@ -1360,7 +1362,7 @@ do
-- This is a hack, we have no basepoints.
correction = details.fontdata.parameters.size / 1000
-- And this needs checking.
- correction = correction * dimenfactors.bp / dimenfactors.pt
+ correction = correction * bpfactor / ptfactor
metadata = { }
end
@@ -1510,114 +1512,296 @@ do
mainwriters["opentype"](details)
end
- -- todo: map pdf glyphs onto companion type 3 font .. can be set of small
- -- ones. maybe only private codes with proper tounicode
+ do
+
+ -- The methods might become plugins.
+
+ local methods = { }
+
+ local pdfimage = lpdf.epdf.image
+ local openpdf = pdfimage.open
+ local closepdf = pdfimage.close
+ local copypage = pdfimage.copy
+
+ local embedimage = images.embed
+
+ local f_glyph = formatters["G%d"]
+ local f_char = formatters["BT /V%d 1 Tf [<%04X>] TJ ET"]
+ local f_width = formatters["%.6N 0 d0"]
+ local f_index = formatters["I%d"]
+ local f_image = formatters["%.6N 0 d0 /%s Do"]
+ local f_stream = formatters["%.6N 0 d0 %s"]
- local methods = { }
+ -- A type 3 font has at most 256 characters and Acrobat also wants a zero slot
+ -- to be filled. We can share a mandate zero slot character.
- function methods.pk(filename)
- local resolution = 600
- local widthfactor = resolution / 72
- local scalefactor = 72 / resolution / 10
- local pkfullname = resolvers.findpk(basedfontname,resolution)
- if not pkfullname or pkfullname == "" then
- return
+ local c_notdef = nil
+ local r_notdef = nil
+ local w_notdef = nil
+ local fontbbox = nil
+
+ -- pk inclusion (not really tested but not really used either)
+
+ function methods.pk(filename)
+ local resolution = 600
+ local widthfactor = resolution / 72
+ local scalefactor = 72 / resolution / 10
+ local pkfullname = resolvers.findpk(basedfontname,resolution)
+ if not pkfullname or pkfullname == "" then
+ return
+ end
+ local readers = fonts.handlers.tfm.readers
+ local result = readers.loadpk(pkfullname)
+ if not result or result.error then
+ return
+ end
+ return result.glyphs, widthfactor / 65536, scalefactor, readers.pktopdf
end
- local readers = fonts.handlers.tfm.readers
- local result = readers.loadpk(pkfullname)
- if not result or result.error then
- return
+
+ -- pdf inclusion
+
+ function methods.pdf(filename,details)
+ local properties = details.properties
+ local pdfshapes = properties.indexdata[1]
+ local pdfdoc = openpdf(pdfshapes.filename)
+ local xforms = pdfdictionary()
+ local nofglyphs = 0
+ if pdfdoc then
+ local function pdftopdf(glyph,width,data)
+ local image = copypage(pdfdoc,glyph)
+ width = 100 * width * bpfactor
+ embedimage(image)
+ nofglyphs = nofglyphs + 1
+ local name = f_glyph(nofglyphs)
+ xforms[name] = pdfreference(image.objnum)
+ local pdf = f_image(width,name)
+ return pdf, width
+ end
+ local function closepdf()
+ -- closepdf(pdfdoc)
+ end
+ local function getresources()
+ return pdfdictionary { XObject = xforms }
+ end
+ return pdfshapes, 1, 0.001, pdftopdf, closepdf, getresources
+ end
end
- return result.glyphs, widthfactor / 65536, scalefactor, readers.pktopdf
- end
- mainwriters["type3"] = function(details)
- local properties = details.properties
- local basefontname = details.basefontname or properties.name
- local askedmethod = "pk"
- local method = methods[askedmethod]
- if not method then
- return
+ -- mps inclusion
+
+ function methods.mps(filename,details)
+ local properties = details.properties
+ local mpshapes = properties.indexdata[1]
+ local function mpstopdf(mp,width,data)
+ local pdf = metapost.simple("metafun",mp,true) -- can be sped up, minifun
+ local width = 100 * width * bpfactor
+ local stream = f_stream(width,pdf)
+ return stream, width
+ end
+ local function getresources()
+ return lpdf.collectedresources {
+ serialize = false,
+ }
+ end
+ return mpshapes, 1, 0.001, mpstopdf, nil, getresources
end
- local glyphs, widthfactor, scalefactor, glyphtopdf = method(basedfontname)
- if not glyphs then
- return
+
+ -- png inclusion
+
+ -- With d1 the image mask is used when given and obeys color. So it is needed for pure bw
+ -- bitmap fonts, so here we really need d0.
+ --
+ -- Acrobat X pro only seems to see the image mask but other viewers are doing it ok. Acrobat
+ -- reader crashes. We really need to add a notdef!
+
+ function methods.png(filename,details)
+ local properties = details.properties
+ local png = properties.png
+ local hash = png.hash
+ local pngshapes = properties.indexdata[1]
+ local xforms = pdfdictionary()
+ local nofglyphs = 0
+ if pngshapes then
+ local function pngtopdf(glyph,width,data)
+ local info = graphics.identifiers.png(glyph.data,"string")
+ local image = lpdf.injectors.png(info,"string")
+ local bbox = image.bbox
+ local llx = bbox[1] * bpfactor
+ local lly = bbox[2] * bpfactor
+ local urx = bbox[3] * bpfactor
+ local ury = bbox[4] * bpfactor
+ width = width * bpfactor / 10
+ embedimage(image)
+ nofglyphs = nofglyphs + 1
+ local name = f_glyph(nofglyphs)
+ xforms[name] = pdfreference(image.objnum)
+ local pdf = f_image(width,name)
+ return pdf, width
+ end
+ local function closepng()
+ pngshapes = nil
+ end
+ local function getresources()
+ return pdfdictionary { XObject = xforms }
+ end
+ return pngshapes, 1, 1, pngtopdf, closepng, getresources
+ end
end
- local parameters = details.parameters
- local object = details.objectnumber
- local factor = parameters.factor -- normally 1
- local f_name = formatters["I%05i"]
- local fontmatrix = pdfarray { scalefactor, 0, 0, scalefactor, 0, 0 }
- local indices,
- minindex,
- maxindex = collectindices(details.fontdata.characters,details.indices)
- local widths = pdfarray()
- local differences = pdfarray()
- local charprocs = pdfdictionary()
- local basefont = pdfconstant(basefontname)
- local llx, lly, urx, ury = 0, 0, 0, 0
- for i=1,maxindex-minindex+1 do
- widths[i] = 0
+
+ function methods.color(filename,details)
+ local colrshapes = details.properties.indexdata[1]
+ local colrvalues = details.properties.indexdata[2]
+ local usedfonts = { }
+ local dd = details.fontdata.descriptions -- temp hack
+ local function colrtopdf(description,wd,data) -- todo: chardata instead of descriptions
+ -- descriptions by index
+ local colorlist = description.colors
+ if colorlist then
+ local dropdata = data.dropin
+ local dropid = dropdata.properties.id
+ local dropunits = dropdata.parameters.units
+ usedfonts[dropid] = dropid
+
+ local w = description.width or 0
+ local s = #colorlist
+ local l = nil
+ local t = { f_width(w) }
+ local n = 1
+ local d = colrvalues[#colrvalues]
+
+ for i=1,s do
+ local entry = colorlist[i]
+ local v = colrvalues[entry.class] or d
+ if v and l ~= v then
+ n = n + 1 ; t[n] = v
+ l = v
+ end
+ local e = dd[entry.slot]
+ if e then
+ n = n + 1 ; t[n] = f_char(dropid,e.index)
+ end
+ end
+ return concat(t," "), w / dropunits
+ end
+ end
+ local function getresources()
+ return lpdf.collectedresources {
+ serialize = false,
+ fonts = usedfonts,
+ fontprefix = "V",
+ }
+ end
+ return colrshapes, 1, 1, colrtopdf, false, getresources
end
- local d = 0
- local lastindex = -0xFFFF
- for index, data in sortedhash(indices) do
- local name = f_name(index)
- local glyph = glyphs[index]
- if glyph then
- local width = widthfactor * data.width
- local stream, lx, ly, ux, uy = glyphtopdf(glyph,width)
- if stream then
- if index - 1 ~= lastindex then
- d = d + 1 ; differences[d] = index
+
+ mainwriters["type3"] = function(details)
+ local properties = details.properties
+ local basefontname = details.basefontname or properties.name
+ local askedmethod = properties.method or "pk"
+ local method = methods[askedmethod] or methods.pk
+ if not method then
+ return
+ end
+ local glyphs, widthfactor, scalefactor,
+ glyphtopdf, reset, getresources = method(basedfontname,details)
+ if not glyphs then
+ return
+ end
+ local parameters = details.parameters
+ local object = details.objectnumber
+ local factor = parameters.factor -- normally 1
+ local fontmatrix = pdfarray { scalefactor, 0, 0, scalefactor, 0, 0 }
+ local indices,
+ minindex,
+ maxindex = collectindices(details.fontdata.characters,details.indices)
+ local widths = pdfarray()
+ local differences = pdfarray()
+ local charprocs = pdfdictionary()
+ local basefont = pdfconstant(basefontname)
+ local d = 0
+ local forcenotdef = minindex > 0
+ local lastindex = -0xFF
+
+ if forcenotdef then
+ widths[0] = 0
+ minindex = 0
+ lastindex = 0
+ d = 2
+ if not c_notdef then
+ w_notdef = 0
+ c_notdef = pdfconstant(".notdef")
+ r_notdef = pdfreference(pdfflushstreamobject("0 0 d0"))
+ end
+ differences[1] = w_notdef
+ differences[2] = c_notdef
+ charprocs[".notdef"] = r_notdef
+ end
+
+ for i=1,maxindex-minindex+1 do
+ widths[i] = 0
+ end
+
+ for index, data in sortedhash(indices) do
+ local name = f_index(index)
+ local glyph = glyphs[index]
+ if glyph then
+ local width = widthfactor * data.width
+ local stream, wd = glyphtopdf(glyph,width,data)
+ if stream then
+ if wd then
+ width = wd
+ end
+ if index - 1 ~= lastindex then
+ d = d + 1 differences[d] = index
+ end
+ lastindex = index
+ d = d + 1 differences[d] = pdfconstant(name)
+ charprocs[name] = pdfreference(pdfflushstreamobject(stream))
+ widths[index-minindex+1] = width
end
- lastindex = index
- d = d + 1 ; differences[d] = pdfconstant(name)
- charprocs[name] = pdfreference(pdfflushstreamobject(stream))
- widths[index-minindex+1] = width
- if lx < llx then llx = lx end
- if ux > urx then urx = ux end
- if ly < lly then lly = ly end
- if uy > ury then ury = uy end
+ else
+ report_fonts("missing glyph %i in type3 font %a",index,basefontname)
end
end
+ if not fontbbox then
+ -- The specification permits zero values and these are actually also more
+ -- robust as then there are no assumptions and no accuracy is needed.
+ fontbbox = pdfarray { 0, 0, 0, 0 }
+ end
+ local encoding = pdfdictionary {
+ Type = pdfconstant("Encoding"),
+ Differences = differences,
+ }
+ local tounicode = tounicodedictionary(details,indices,maxindex,basefontname)
+ local resources = getresources and getresources() or lpdf.procset(true)
+ local descriptor = pdfdictionary {
+ -- most is optional in type3
+ Type = pdfconstant("FontDescriptor"),
+ FontName = basefont,
+ Flags = 4,
+ ItalicAngle = 0,
+ }
+ local parent = pdfdictionary {
+ Type = pdfconstant("Font"),
+ Subtype = pdfconstant("Type3"),
+ Name = basefont,
+ FontBBox = fontbbox,
+ FontMatrix = fontmatrix,
+ CharProcs = pdfreference(pdfflushobject(charprocs)),
+ Encoding = pdfreference(pdfflushobject(encoding)),
+ FirstChar = minindex,
+ LastChar = maxindex,
+ Widths = pdfreference(pdfflushobject(widths)),
+ FontDescriptor = pdfreference(pdfflushobject(descriptor)),
+ Resources = resources,
+ ToUnicode = pdfreference(pdfflushstreamobject(tounicode)),
+ }
+ pdfflushobject(object,parent)
+ if reset then
+ reset()
+ end
end
- local fontbbox = pdfarray { llx, lly, urx, ury }
- local encoding = pdfdictionary {
- Type = pdfconstant("Encoding"),
- Differences = differences,
- }
- local tounicode = tounicodedictionary(details,indices,maxindex,basefontname)
- local descriptor = pdfdictionary {
- Type = pdfconstant("FontDescriptor"),
- FontName = basefont,
- Flags = 4,
- FontBBox = fontbbox,
- -- Ascent = scale(ascender),
- -- Descent = scale(descender),
- -- ItalicAngle = round(italicangle or 0),
- -- CapHeight = scale(capheight),
- -- StemV = scale(stemv),
- -- XHeight = scale(xheight),
- -- Metadata = fontmeta and pdfreference(pdfflushstreamobject(fontmeta)) or nil,
- }
- local parent = pdfdictionary {
- Type = pdfconstant("Font"),
- Subtype = pdfconstant("Type3"),
- Name = basefont,
- FontBBox = fontbbox,
- FontMatrix = fontmatrix,
- CharProcs = pdfreference(pdfflushobject(charprocs)),
- Encoding = pdfreference(pdfflushobject(encoding)),
- FirstChar = minindex,
- LastChar = maxindex,
- Widths = pdfreference(pdfflushobject(widths)),
- FontDescriptor = pdfreference(pdfflushobject(descriptor)),
- Resources = lpdf.procset(true),
- ToUnicode = pdfreference(pdfflushstreamobject(tounicode)),
- }
- pdfflushobject(reserved,descriptor)
- pdfflushobject(object,parent)
+
end
end
@@ -1650,7 +1834,8 @@ local objects = setmetatableindex(function(t,k)
report_fonts("font id %i bound to hash %s and object %i",k,h,v)
end
else
- report_fonts("fatal error, hash %s asked but not used",k,h,v)
+ -- no problem as it can be svg only
+ -- report_fonts("fatal error, hash %s asked but not used",k,h,v)
v = pdfreserveobject()
t[k] = v
end
@@ -1752,7 +1937,8 @@ function lpdf.flushfonts()
end
local properties = details.properties
local bitmap = properties.usedbitmap
- if bitmap then
+ local method = properties.method -- will be pk | pdf | svg | ...
+ if bitmap or method then
local format = "type3"
local writer = mainwriters[format]
if writer then
@@ -1786,7 +1972,7 @@ function lpdf.flushfonts()
local vector = encoding.vector
local indices = details.indices
local remapped = { }
- local factor = dimenfactors.bp * size / 65536
+ local factor = bpfactor * size / 65536
for k, v in next, indices do
local name = vector[k]
local index = reverse[name] or 0
@@ -1826,7 +2012,7 @@ function lpdf.flushfonts()
local vector = encoding.vector
local indices = details.indices
local remapped = { }
- local factor = dimenfactors.bp * size / 65536
+ local factor = bpfactor * size / 65536
for k, v in next, indices do
local name = vector[k]
local index = reverse[name] or 0
@@ -1868,8 +2054,11 @@ function lpdf.flushfonts()
report_fonts("no %a writer for %a",format,filename)
end
end
- else
- report_fonts("no indices for %a",filename)
+ else -- not problem for svg ...
+ -- report_fonts("no indices for %a",filename)
+ end
+ if trace_fonts then
+ report_fonts("embedded indices: % t",table.sortedkeys(details.indices))
end
mainfonts[details.hash] = false -- done
end
diff --git a/tex/context/base/mkiv/lpdf-img.lua b/tex/context/base/mkiv/lpdf-img.lua
index 64ed642fa..6b19f0cfd 100644
--- a/tex/context/base/mkiv/lpdf-img.lua
+++ b/tex/context/base/mkiv/lpdf-img.lua
@@ -33,6 +33,8 @@ local openstring = streams.openstring
local readstring = streams.readstring
local readbytetable = streams.readbytetable
+local newreader = io.newreader
+
local tobytetable = string.bytetable
local lpdf = lpdf or { }
@@ -871,7 +873,7 @@ do
end
end)
- function injectors.png(specification)
+ function injectors.png(specification,method) -- todo: method in specification
-- inspect(specification)
if specification.error then
return
@@ -901,7 +903,7 @@ do
if not idat then
return
end
- local pngfile = io.open(filename,"rb") -- todo: in-mem too
+ local pngfile = newreader(filename,method)
if not pngfile then
return
end
@@ -1094,8 +1096,10 @@ do
if specification.colorref then
xobject.ColorSpace = pdfreference(specification.colorref)
end
+ local width = specification.width or xsize * 65536
+ local height = specification.height or ysize * 65536
return createimage {
- bbox = { 0, 0, specification.width/xsize, specification.height/ysize }, -- mandate
+ bbox = { 0, 0, width/xsize, height/ysize }, -- mandate
transform = specification.transform,
nolength = true,
nobbox = true,
@@ -1204,14 +1208,14 @@ do
local content = pack(specification,index and "indexed" or "data")
local mask = specification.mask
local colorspace = pdfconstant(colorspace)
-if index then
- colorspace = pdfarray {
- pdfconstant("Indexed"),
- colorspace,
- #index + (index[0] and 0 or -1), -- upper index
- pdfverbose(pack(specification,"index"))
- }
-end
+ if index then
+ colorspace = pdfarray {
+ pdfconstant("Indexed"),
+ colorspace,
+ #index + (index[0] and 0 or -1), -- upper index
+ pdfverbose(pack(specification,"index"))
+ }
+ end
local xobject = pdfdictionary {
Type = pdfconstant("XObject"),
Subtype = pdfconstant("Image"),
diff --git a/tex/context/base/mkiv/lpdf-ini.lua b/tex/context/base/mkiv/lpdf-ini.lua
index 19654a3d9..fd1e52aad 100644
--- a/tex/context/base/mkiv/lpdf-ini.lua
+++ b/tex/context/base/mkiv/lpdf-ini.lua
@@ -12,7 +12,7 @@ local setmetatable, getmetatable, type, next, tostring, tonumber, rawset = setme
local char, byte, format, gsub, concat, match, sub, gmatch = string.char, string.byte, string.format, string.gsub, table.concat, string.match, string.sub, string.gmatch
local utfchar, utfbyte, utfvalues = utf.char, utf.byte, utf.values
local sind, cosd, max, min = math.sind, math.cosd, math.max, math.min
-local sort = table.sort
+local sort, sortedhash = table.sort, table.sortedhash
local P, C, R, S, Cc, Cs, V = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cc, lpeg.Cs, lpeg.V
local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
local formatters = string.formatters
@@ -170,16 +170,16 @@ end
local pdfgetpagereference
updaters.register("backend.update.lpdf",function()
- pdfreserveobject = pdf.reserveobj
- pdfimmediateobject = pdf.immediateobj
- pdfdeferredobject = pdf.obj
- pdfreferenceobject = pdf.refobj
+ pdfreserveobject = pdf.reserveobj
+ pdfimmediateobject = pdf.immediateobj
+ pdfdeferredobject = pdf.obj
+ pdfreferenceobject = pdf.refobj
- pdfgetpagereference = pdf.getpageref
+ pdfgetpagereference = pdf.getpageref
- pdfsetpageresources = pdf.setpageresources
- pdfsetpageattributes = pdf.setpageattributes
- pdfsetpagesattributes = pdf.setpagesattributes
+ pdfsetpageresources = pdf.setpageresources
+ pdfsetpageattributes = pdf.setpageattributes
+ pdfsetpagesattributes = pdf.setpagesattributes
end)
local jobpositions = job.positions
@@ -1253,32 +1253,47 @@ do
-- todo: share them
-- todo: force when not yet set
+ local f_font = formatters["%s%d"]
+
function lpdf.collectedresources(options)
local ExtGState = d_extgstates and next(d_extgstates ) and p_extgstates
local ColorSpace = d_colorspaces and next(d_colorspaces) and p_colorspaces
local Pattern = d_patterns and next(d_patterns ) and p_patterns
local Shading = d_shades and next(d_shades ) and p_shades
+ local Font
if options and options.patterns == false then
Pattern = nil
end
- if ExtGState or ColorSpace or Pattern or Shading then
+ local fonts = options and options.fonts
+ if fonts and next(fonts) then
+ local pdfgetfontobjnumber = lpdf.getfontobjnumber
+ if pdfgetfontobjnumber then
+ local prefix = options.fontprefix or "F"
+ Font = pdfdictionary { }
+ for k, v in sortedhash(fonts) do
+ Font[f_font(prefix,v)] = pdfreference(pdfgetfontobjnumber(k))
+ end
+ end
+ end
+ if ExtGState or ColorSpace or Pattern or Shading or Fonts then
local collected = pdfdictionary {
ExtGState = ExtGState,
ColorSpace = ColorSpace,
Pattern = Pattern,
Shading = Shading,
+ Font = Font,
}
if options and options.serialize == false then
return collected
else
return collected()
end
+ elseif options and options.notempty then
+ return nil
+ elseif options and options.serialize == false then
+ return pdfdictionary { }
else
- if options and options.serialize == false then
- return pdfdictionary { }
- else
- return ""
- end
+ return ""
end
end
@@ -1487,41 +1502,23 @@ end
do
- local f_actual_text_one = formatters["BT /Span << /ActualText <feff%04x> >> BDC %s EMC ET"]
- local f_actual_text_two = formatters["BT /Span << /ActualText <feff%04x%04x> >> BDC %s EMC ET"]
- local f_actual_text_one_b = formatters["BT /Span << /ActualText <feff%04x> >> BDC"]
- local f_actual_text_two_b = formatters["BT /Span << /ActualText <feff%04x%04x> >> BDC"]
+ local f_actual_text_p = formatters["BT /Span << /ActualText <feff%s> >> BDC %s EMC ET"]
local f_actual_text_b = formatters["BT /Span << /ActualText <feff%s> >> BDC"]
local s_actual_text_e = "EMC ET"
local f_actual_text_b_not = formatters["/Span << /ActualText <feff%s> >> BDC"]
- local f_actual_text_one_b_not = formatters["/Span << /ActualText <feff%04x> >> BDC"]
- local f_actual_text_two_b_not = formatters["/Span << /ActualText <feff%04x%04x> >> BDC"]
local s_actual_text_e_not = "EMC"
local f_actual_text = formatters["/Span <</ActualText %s >> BDC"]
local context = context
local pdfdirect = nodes.pool.directliteral -- we can use nuts.write deep down
-
- -- todo: use tounicode from the font mapper
-
- -- floor(unicode/1024) => rshift(unicode,10) -- better for 5.3
+ local tounicode = fonts.mappings.tounicode
function codeinjections.unicodetoactualtext(unicode,pdfcode)
- if unicode < 0x10000 then
- return f_actual_text_one(unicode,pdfcode)
- else
- return f_actual_text_two(rshift(unicode,10)+0xD800,unicode%1024+0xDC00,pdfcode)
- end
+ return f_actual_text_p(type(unicode) == "string" and unicode or tounicode(unicode),pdfcode)
end
function codeinjections.startunicodetoactualtext(unicode)
- if type(unicode) == "string" then
- return f_actual_text_b(unicode)
- elseif unicode < 0x10000 then
- return f_actual_text_one_b(unicode)
- else
- return f_actual_text_two_b(rshift(unicode,10)+0xD800,unicode%1024+0xDC00)
- end
+ return f_actual_text_b(type(unicode) == "string" and unicode or tounicode(unicode))
end
function codeinjections.stopunicodetoactualtext()
@@ -1529,13 +1526,7 @@ do
end
function codeinjections.startunicodetoactualtextdirect(unicode)
- if type(unicode) == "string" then
- return f_actual_text_b_not(unicode)
- elseif unicode < 0x10000 then
- return f_actual_text_one_b_not(unicode)
- else
- return f_actual_text_two_b_not(rshift(unicode,10)+0xD800,unicode%1024+0xDC00)
- end
+ return f_actual_text_b_not(type(unicode) == "string" and unicode or tounicode(unicode))
end
function codeinjections.stopunicodetoactualtextdirect()
diff --git a/tex/context/base/mkiv/lpdf-lmt.lua b/tex/context/base/mkiv/lpdf-lmt.lua
index 709a9d48a..8b40ee15c 100644
--- a/tex/context/base/mkiv/lpdf-lmt.lua
+++ b/tex/context/base/mkiv/lpdf-lmt.lua
@@ -37,6 +37,7 @@ local formatters, splitupstring = string.formatters, string.splitup
local band, extract = bit32.band, bit32.extract
local concat, sortedhash = table.concat, table.sortedhash
local setmetatableindex = table.setmetatableindex
+local loaddata = io.loaddata
local bpfactor <const> = number.dimenfactors.bp
@@ -49,7 +50,6 @@ local tonut = nodes.tonut
local getdata = nuts.getdata
local getsubtype = nuts.getsubtype
-local getfield = nuts.getfield
local getwhd = nuts.getwhd
local flushlist = nuts.flush_list
@@ -583,6 +583,15 @@ local flushcharacter do
end
+ flushfontchar = function(font,char,data)
+ local dummy = usedfonts[font]
+ local index = data.index or char
+ if not pdfcharacters[index] then
+ pdfcharacters[index] = true
+ end
+ return dummy
+ end
+
end
-- literals
@@ -1410,18 +1419,26 @@ local function finalize(driver,details)
pageresources.XObject = xforms
pageresources.ProcSet = lpdf.procset()
+ local xorigin, yorigin, relocated = backends.codeinjections.getpageorigin() -- for now here
+
+ local bbox = pdfarray {
+ (boundingbox[1] + xorigin) * bpfactor,
+ (boundingbox[2] + yorigin) * bpfactor,
+ (boundingbox[3] + xorigin) * bpfactor,
+ (boundingbox[4] + yorigin) * bpfactor,
+ }
+
+ if relocated then
+ content = formatters["1 0 0 1 %.6N %.6N cm\n%s"](bbox[1],bbox[2],content)
+ end
+
local contentsobj = pdfflushstreamobject(content,false,false)
pageattributes.Type = pdf_page
pageattributes.Contents = pdfreference(contentsobj)
pageattributes.Resources = pageresources
-- pageattributes.Resources = pdfreference(pdfflushobject(pageresources))
- pageattributes.MediaBox = pdfarray {
- boundingbox[1] * bpfactor,
- boundingbox[2] * bpfactor,
- boundingbox[3] * bpfactor,
- boundingbox[4] * bpfactor,
- }
+ pageattributes.MediaBox = bbox
pageattributes.Parent = nil -- precalculate
pageattributes.Group = nil -- todo
@@ -1431,6 +1448,12 @@ local function finalize(driver,details)
lpdf.finalizepage(true)
+ if relocated then
+ if pageattributes.TrimBox then pageattributes.TrimBox = box end
+ if pageattributes.CropBox then pageattributes.CropBox = box end
+ if pageattributes.BleedBox then pageattributes.BleedBox = box end
+ end
+
else
local xformtype = specification.type or 0
@@ -1850,18 +1873,18 @@ local function obj(a,b,c,d)
nolength = a.nolength
if kind == "stream" then
if filename then
- data = io.loaddata(filename) or ""
+ data = loaddata(filename) or ""
end
elseif kind == "raw"then
if filename then
- data = io.loaddata(filename) or ""
+ data = loaddata(filename) or ""
end
elseif kind == "file"then
kind = "raw"
- data = filename and io.loaddata(filename) or ""
+ data = filename and loaddata(filename) or ""
elseif kind == "streamfile" then
kind = "stream"
- data = filename and io.loaddata(filename) or ""
+ data = filename and loaddata(filename) or ""
end
else
if argtype == "number" then
@@ -1877,10 +1900,10 @@ local function obj(a,b,c,d)
data = b
elseif a == "file" then
-- kind = "raw"
- data = io.loaddata(b)
+ data = loaddata(b)
elseif a == "streamfile" then
kind = "stream"
- data = io.loaddata(b)
+ data = loaddata(b)
else
data = "" -- invalid object
end
@@ -2312,10 +2335,13 @@ end)
updaters.register("backend.update.lpdf",function()
- -- for the moment here, todo: an md5 or sha2 hash can save space
+ -- todo: an md5 or sha2 hash can save space
+ -- todo: make a type 3 font instead
+ -- todo: move to lpdf namespace
local pdfimage = lpdf.epdf.image
local newpdf = pdfimage.new
+ local openpdf = pdfimage.open
local closepdf = pdfimage.close
local copypage = pdfimage.copy
@@ -2325,7 +2351,7 @@ updaters.register("backend.update.lpdf",function()
local topdf = { }
local toidx = { }
- local function storedata(pdf)
+ local function storedata_s(pdf)
local idx = toidx[pdf]
if not idx then
nofstreams = nofstreams + 1
@@ -2336,11 +2362,7 @@ updaters.register("backend.update.lpdf",function()
return idx
end
- -- todo: make a type 3 font instead
-
- -- move to lpdf namespace
-
- local function vfimage(id,wd,ht,dp,pos_h,pos_v)
+ local function vfimage_s(id,wd,ht,dp,pos_h,pos_v)
local index = topdf[id]
if type(index) == "string" then
local pdfdoc = newpdf(index,#index)
@@ -2356,11 +2378,51 @@ updaters.register("backend.update.lpdf",function()
flushimage(index,wd,ht,dp,pos_h,pos_v)
end
+ local function storedata_n(name,page)
+ local idx = toidx[pdf]
+ if not idx then
+ nofstreams = nofstreams + 1
+ idx = nofstreams
+ toidx[pdf] = nofstreams
+ topdf[idx] = pdf
+ end
+ return idx
+ end
+
+ -- We need to have a way to close such a pdf ... esp for fonts.
+
+ local pdfdocs = { }
+
+ local function vfimage_n(name,page,wd,ht,dp,pos_h,pos_v)
+ local d = pdfdocs[name]
+ if not d then
+ d = { doc = openpdf(name), pages = { } }
+ pdfdocs[name] = d
+ end
+ local index = d.pages[page]
+ if not index then
+ local image = copypage(d.doc,page)
+ local bbox = image.bbox
+ image.width = bbox[3] - bbox[1]
+ image.height = bbox[4] - bbox[2]
+ embedimage(image)
+ index = image.index
+ d.pages[page] = index
+ end
+ flushimage(index,wd,ht,dp,pos_h,pos_v)
+ end
+
local function pdfvfimage(wd,ht,dp,data,name)
- return { "lua", function(font,char,pos_h,pos_v)
- local id = storedata(data)
- vfimage(id,wd,ht,dp,pos_h,pos_v)
- end }
+ if type(data) == "number" then
+ return { "lua", function(font,char,pos_h,pos_v)
+ vfimage_n(name,data,wd,ht,dp,pos_h,pos_v)
+ end }
+ else
+ return { "lua", function(font,char,pos_h,pos_v)
+ local id = storedata_s(data)
+ vfimage_s(id,wd,ht,dp,pos_h,pos_v)
+ end }
+ end
end
lpdf.vfimage = pdfvfimage
@@ -2482,6 +2544,7 @@ do
name = "pdf",
flushers = {
character = flushcharacter,
+ fontchar = flushfontchar,
rule = flushrule,
simplerule = flushsimplerule,
pushorientation = pushorientation,
diff --git a/tex/context/base/mkiv/lxml-css.lua b/tex/context/base/mkiv/lxml-css.lua
index b0f5c9b72..8f7f19e84 100644
--- a/tex/context/base/mkiv/lxml-css.lua
+++ b/tex/context/base/mkiv/lxml-css.lua
@@ -146,9 +146,11 @@ if context then
end
+local p_digit = lpeg.patterns.digit
+
local pattern = Cf( Ct("") * (
Cg(
- Cc("style") * (
+ Cc("style") * (
C("italic")
+ C("oblique")
+ C("slanted") / "oblique"
@@ -156,12 +158,17 @@ local pattern = Cf( Ct("") * (
+ Cc("variant") * (
(C("smallcaps") + C("caps")) / "small-caps"
)
- + Cc("weight") *
+ + Cc("weight") * (
C("bold")
- + Cc("family") * (
- (C("mono") + C("type")) / "monospace" -- just ignore the "space(d)"
+ )
+ + Cc("family") * (
+ (C("mono") + C("type")) / "monospace" -- just ignore the "space(d)"
+ (C("sansserif") + C("sans")) / "sans-serif" -- match before serif
- + C("serif")
+ + C("serif")
+ )
+ + Cc("size") * Ct (
+ (S("+-")^0 * (p_digit^0 * P(".") * p_digit^1 + p_digit^1 * P(".") + p_digit^1)) / tonumber
+ * C(P("p") * S("txc") + P("e") * S("xm") + S("mc") * P("m") + P("in") + P("%"))
)
)
--+ P("\\") * (
diff --git a/tex/context/base/mkiv/meta-fig.mkiv b/tex/context/base/mkiv/meta-fig.mkiv
index bf37aa7bf..e89cb1442 100644
--- a/tex/context/base/mkiv/meta-fig.mkiv
+++ b/tex/context/base/mkiv/meta-fig.mkiv
@@ -44,12 +44,17 @@
\definefittingpage
[MPpage]
[\c!align=,
- \c!command=\meta_process_graphic_instance{\fittingpageparameter\c!instance},
+ \c!command=\meta_process_graphic_command,
\c!instance=]
\unexpanded\def\setupMPpage
{\setupfittingpage[MPpage]}
+\unexpanded\def\meta_process_graphic_command
+ {\doif{\fittingpageparameter\c!alternative}\v!offset
+ {\def\meta_relocate_graphic{\clf_setpageorigin\MPllx\MPlly\relax}}%
+ \meta_process_graphic_instance{\fittingpageparameter\c!instance}}
+
%D \macros
%D {MPfigure}
%D
diff --git a/tex/context/base/mkiv/meta-ini.mkiv b/tex/context/base/mkiv/meta-ini.mkiv
index fa5aa92c6..97d2e1a7d 100644
--- a/tex/context/base/mkiv/meta-ini.mkiv
+++ b/tex/context/base/mkiv/meta-ini.mkiv
@@ -278,9 +278,12 @@
{\pushMPboundingbox
\setbox\b_meta_graphic\hpack\bgroup}
+\let\meta_relocate_graphic\relax % experimental hook
+
\def\meta_process_graphic_stop
{\egroup
\meta_place_graphic
+ \meta_relocate_graphic
\popMPboundingbox}
\unexpanded\def\meta_process_graphic_instance#1%
diff --git a/tex/context/base/mkiv/meta-ini.mkxl b/tex/context/base/mkiv/meta-ini.mkxl
index 31117420c..745bd9268 100644
--- a/tex/context/base/mkiv/meta-ini.mkxl
+++ b/tex/context/base/mkiv/meta-ini.mkxl
@@ -277,9 +277,12 @@
{\pushMPboundingbox
\setbox\b_meta_graphic\hpack\bgroup}
+\let\meta_relocate_graphic\relax % experimental hook
+
\def\meta_process_graphic_stop
{\egroup
\meta_place_graphic
+ \meta_relocate_graphic
\popMPboundingbox}
\unexpanded\def\meta_process_graphic_instance#1%
diff --git a/tex/context/base/mkiv/mlib-cnt.lua b/tex/context/base/mkiv/mlib-cnt.lua
new file mode 100644
index 000000000..5bd27dbfd
--- /dev/null
+++ b/tex/context/base/mkiv/mlib-cnt.lua
@@ -0,0 +1,1770 @@
+if not modules then modules = { } end modules ['mlib-cnt'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- The only useful reference that I could find about this topic is in wikipedia:
+--
+-- https://en.wikipedia.org/wiki/Marching_squares
+--
+-- I derived the edge code from:
+--
+-- https://physiology.arizona.edu/people/secomb/contours
+--
+-- and also here:
+--
+-- https://github.com/secomb/GreensV4
+--
+-- which has the banner:
+--
+-- TWS, November 1989. Converted to C September 2007. Revised February 2009.
+--
+-- and has a liberal licence. I optimized the code so that it runs a bit faster in Lua and
+-- there's probably more room for optimization (I tried several variants). For instance I
+-- don't use that many tables because access is costly. We don't have a compiler that does
+-- some optimizing (even then the c code can probably be made more efficient).
+--
+-- We often have the same node so it's cheaper to reuse the wsp tables and reconstruct
+-- the point in the path then to alias the original point. We can be more clever:
+-- straighten, but it's more work so maybe I will do that later; for now I only added
+-- a test for equality. There are some experiments in this file too and not all might
+-- work. It's a playground for me.
+--
+-- The code is meant for metafun so it is not general purpose. The bitmap variant is
+-- relatively fast and bitmaps compress well. When all is settled I might make a couple
+-- of helpers in C for this purpose but not now.
+--
+-- I removed optimization code (more aggressive flattening and such because it didn't really
+-- pay off now that we use combined paths with just line segments. I also moved some other
+-- code to a experimental copy. So we now only have the bare helpers needed here.
+
+-- Todo: look into zero case (lavel 1) for shapes ... omiting the background calculation
+-- speeds up quite a bit.
+
+local next, type, tostring = next, type, tostring
+local round, abs, min, max, floor = math.round, math.abs, math.min, math.max, math.floor
+local concat, move = table.concat, table.move
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+local elapsedtime = statistics.elapsedtime
+
+local formatters = string.formatters
+local setmetatableindex = table.setmetatableindex
+local sortedkeys = table.sortedkeys
+local sequenced = table.sequenced
+
+local metapost = metapost or { }
+local mp = mp or { }
+
+local getparameterset = metapost.getparameterset
+
+local mpflush = mp.flush
+local mptriplet = mp.triplet
+local mpstring = mp.string
+local mpdraw = mp.draw
+local mpfill = mp.fill
+local mpflatten = mp.flatten
+
+local report = logs.reporter("mfun contour")
+
+local version = 0.007
+
+-- we iterate using integers so that we get a better behaviour at zero
+
+local f_function_n = formatters [ [[
+ local math = math
+ local round = math.round
+ %s
+ return function(data,nx,ny,nxmin,nxmax,xstep,nymin,nymax,ystep)
+ local sx = nxmin
+ for mx=1,nx do
+ local dx = data[mx]
+ local x = sx * xstep
+ local sy = nymin
+ for my=1,ny do
+ local y = sy * ystep
+ dx[my] = %s
+ sy = sy + 1
+ end
+ sx = sx + 1
+ end
+ return 0
+ end
+]] ]
+
+local f_function_y = formatters [ [[
+ local math = math
+ local round = math.round
+ local nan = NaN
+ local inf = math.huge
+ %s
+ return function(data,nx,ny,nxmin,nxmax,xstep,nymin,nymax,ystep,dnan,dinf,report)
+ local sx = nxmin
+ local er = 0
+ for mx=nxmin,nxmax do
+ local dx = data[mx]
+ local x = sx * xstep
+ local sy = nymin
+ for my=nymin,nymax do
+ local y = sy * ystep
+ local n = %s
+ if n == nan then
+ er = er + 1
+ if er < 10 then
+ report("nan at (%s,%s) -> ",x,y,mx,my)
+ end
+ n = dnan
+ elseif n == inf then
+ er = er + 1
+ if er < 10 then
+ report("inf at (%s,%s) -> ",x,y,mx,my)
+ end
+ n = dinf
+ end
+ dx[my] = n
+ sy = sy + 1
+ end
+ sx = sx + 1
+ end
+ return er
+ end
+]] ]
+
+local f_color = formatters [ [[
+ local math = math
+ local min = math.min
+ local max = math.max
+ local n = %s
+ local minz = %s
+ local maxz = %s
+
+ local color_value = 0
+ local color_step = mp.lmt_color_functions.step
+ local color_shade = mp.lmt_color_functions.shade
+
+ local function step(...)
+ return color_step(color_value,n,...)
+ end
+ local function shade(...)
+ return color_shade(color_value,n,...)
+ end
+ local function lin(l)
+ return l/n
+ end
+ %s
+ return function(l)
+ color_value = l
+ return %s
+ end
+]] ]
+
+local inbetween = attributes and attributes.colors.helpers.inbetween
+
+mp.lmt_color_functions = {
+ step = function(l,n,r,g,b,s)
+ if not s then
+ s = 1
+ end
+ local f = l / n
+ local r = s * 1.5 - 4 * abs(f-r)
+ local g = s * 1.5 - 4 * abs(f-g)
+ local b = s * 1.5 - 4 * abs(f-b)
+ return min(max(r,0),1), min(max(g,0),1), min(max(b,0),1)
+ end,
+ shade = function(l,n,one,two)
+ local f = l / n
+ local r = inbetween(one,two,1,f)
+ local g = inbetween(one,two,2,f)
+ local b = inbetween(one,two,3,f)
+ return min(max(r,0),1), min(max(g,0),1), min(max(b,0),1)
+ end,
+}
+
+local f_box = formatters [ [[draw rawtexbox("contour",%s) xysized (%s,%s) ;]] ]
+
+local n_box = 0
+
+-- todo: remove old one, so we need to store old hashes
+
+local nofcontours = 0
+
+-- We don't want cosmetics like axis and labels to trigger a calculation,
+-- especially a slow one.
+
+local hashfields = {
+ "xmin", "xmax", "xstep", "ymin", "ymax", "ystep",
+ "levels", "colors", "level", "preamble", "function", "functions", "color", "values",
+ "background", "foreground", "linewidth", "backgroundcolor", "linecolor",
+}
+
+local function prepare(p)
+ local h = { }
+ for i=1,#hashfields do
+ local f = hashfields[i]
+ h[f] = p[f]
+ end
+ local hash = md5.HEX(sequenced(h))
+ local name = formatters["%s-m-c-%03i.lua"](tex.jobname,nofcontours)
+ return name, hash
+end
+
+local function getcache(p)
+ local cache = p.cache
+ if cache then
+ local name, hash = prepare(p)
+ local data = table.load(name)
+ if data and data.hash == hash and data.version == version then
+ p.result = data
+ return true
+ else
+ return false, hash, name
+ end
+ end
+ return false, nil, nil
+end
+
+local function setcache(p)
+ local result = p.result
+ local name = result.name
+ if name then
+ if result.bitmap then
+ result.bitmap = nil
+ else
+ result.data = nil
+ end
+ result.color = nil
+ result.action = nil
+ result.cached = nil
+ io.savedata(name, table.fastserialize(result))
+ else
+ os.remove((prepare(p)))
+ end
+end
+
+function mp.lmt_contours_start()
+
+ starttiming("lmt_contours")
+
+ nofcontours = nofcontours + 1
+
+ local p = getparameterset()
+
+ local xmin = p.xmin
+ local xmax = p.xmax
+ local ymin = p.ymin
+ local ymax = p.ymax
+ local xstep = p.xstep
+ local ystep = p.ystep
+ local levels = p.levels
+ local colors = p.colors
+ local nx = 0
+ local ny = 0
+ local means = setmetatableindex("number")
+ local values = setmetatableindex("number")
+ local data = setmetatableindex("table")
+ local minmean = false
+ local maxmean = false
+
+ local cached, hash, name = getcache(p)
+
+ local function setcolors(preamble,levels,minz,maxz,color,nofvalues)
+ if colors then
+ local function f(k)
+ return #colors[1] == 1 and 0 or { 0, 0, 0 }
+ end
+ setmetatableindex(colors, function(t,k)
+ local v = f(k)
+ t[k] = v
+ return v
+ end)
+ local c = { }
+ local n = 1
+ for i=1,nofvalues do
+ c[i] = colors[n]
+ n = n + 1
+ end
+ return c, f
+ else
+ local tcolor = f_color(levels,minz,maxz,preamble,color)
+ local colors = { }
+ local fcolor = load(tcolor)
+ if type(fcolor) ~= "function" then
+ report("error in color function, case %i: %s",1,color)
+ fcolor = false
+ else
+ fcolor = fcolor()
+ if type(fcolor) ~= "function" then
+ report("error in color function, case %i: %s",2,color)
+ fcolor = false
+ end
+ end
+ if not fcolor then
+ local color_step = mp.lmt_color_functions.step
+ fcolor = function(l)
+ return color_step(l,levels,0.25, 0.50, 0.75)
+ end
+ end
+ for i=1,nofvalues do
+ colors[i] = { fcolor(i) }
+ end
+ return colors, fcolor
+ end
+ end
+
+ if cached then
+ local result = p.result
+ local colors, color = setcolors(
+ p.preamble,
+ p.levels,
+ result.minz,
+ result.maxz,
+ p.color,
+ result.nofvalues
+ )
+ result.color = color
+ result.colors = colors
+ result.cached = true
+ return
+ end
+
+ local functioncode = p["function"]
+ local functionrange = p.range
+ local functionlist = functionrange and p.functions
+ local preamble = p.preamble
+
+ if xstep == 0 then xstep = (xmax - xmin)/200 end
+ if ystep == 0 then ystep = (ymax - ymin)/200 end
+
+ local nxmin = round(xmin/xstep)
+ local nxmax = round(xmax/xstep)
+ local nymin = round(ymin/ystep)
+ local nymax = round(ymax/ystep)
+ local nx = nxmax - nxmin + 1
+ local ny = nymax - nymin + 1
+
+ local function executed(data,code)
+ local fcode = p.check and f_function_y or f_function_n
+ fcode = fcode(preamble,code)
+ fcode = load(fcode)
+ if type(fcode) == "function" then
+ fcode = fcode()
+ end
+ if type(fcode) == "function" then
+ local er = fcode(
+ data, nx, ny,
+ nxmin, nxmax, xstep,
+ nymin, nymax, ystep,
+ p.defaultnan, p.defaultinf, report
+ )
+ if er > 0 then
+ report("%i errors in: %s",er,code)
+ end
+ return true
+ else
+ return false -- fatal error
+ end
+ end
+
+
+ -- for i=1,nx do
+ -- data[i] = lua.newtable(ny,0)
+ -- end
+
+ if functionlist then
+
+ local datalist = { }
+ local minr = functionrange[1]
+ local maxr = functionrange[2] or minr
+ local size = #functionlist
+
+ for l=1,size do
+
+ local func = setmetatableindex("table")
+ if not executed(func,functionlist[l]) then
+ report("error in executing function %i from functionlist",l)
+ return
+ end
+
+ local bit = l -- + 1
+
+ if l == 1 then
+ for i=1,nx do
+ local di = data[i]
+ local fi = func[i]
+ for j=1,ny do
+ local f = fi[j]
+ if f >= minr and f <= maxr then
+ di[j] = bit
+ else
+ di[j] = 0
+ end
+ end
+ end
+ else
+ for i=1,nx do
+ local di = data[i]
+ local fi = func[i]
+ for j=1,ny do
+ local f = fi[j]
+ if f >= minr and f <= maxr then
+ di[j] = di[j] | bit
+ end
+ end
+ end
+ end
+
+ end
+
+ -- we can simplify the value mess below
+
+ elseif functioncode then
+
+ if not executed(data,functioncode) then
+ report("error in executing function")
+ return -- fatal error
+ end
+
+ else
+
+ report("no valid function(s)")
+ return -- fatal error
+
+ end
+
+ minz = data[1][1]
+ maxz = minz
+
+ for i=1,nx do
+ local d = data[i]
+ for j=1,ny do
+ local v = d[j]
+ if v < minz then
+ minz = v
+ elseif v > maxz then
+ maxz = v
+ end
+ end
+ end
+
+ if functionlist then
+
+ for i=minz,maxz do
+ values[i] = i
+ end
+
+ levels = maxz
+
+ minmean = minz
+ maxmean = maxz
+
+ else
+
+ local snap = (maxz - minz + 1) / levels
+
+ for i=1,nx do
+ local d = data[i]
+ for j=1,ny do
+ local dj = d[j]
+ local v = round((dj - minz) / snap)
+ values[v] = values[v] + 1
+ means [v] = means [v] + dj
+ d[j] = v
+ end
+ end
+
+ local list = sortedkeys(values)
+ local count = values
+ local remap = { }
+
+ values = { }
+
+ for i=1,#list do
+ local v = list[i]
+ local m = means[v] / count[v]
+ remap [v] = i
+ values[i] = m
+ if not minmean then
+ minmean = m
+ maxmean = m
+ elseif m < minmean then
+ minmean = m
+ elseif m > maxmean then
+ maxmean = m
+ end
+ end
+
+ for i=1,nx do
+ local d = data[i]
+ for j=1,ny do
+ d[j] = remap[d[j]]
+ end
+ end
+
+ end
+
+ -- due to rounding we have values + 1 so we can have a floor, ceil, round
+ -- option as well as levels -1
+
+ local nofvalues = #values
+
+ local colors = setcolors(
+ p.preamble,levels,minz,maxz,p.color,nofvalues
+ )
+
+ p.result = {
+ version = version,
+ values = values,
+ nofvalues = nofvalues,
+ minz = minz,
+ maxz = maxz,
+ minmean = minmean,
+ maxmean = maxmean,
+ data = data,
+ color = color,
+ nx = nx,
+ ny = ny,
+ colors = colors,
+ name = name,
+ hash = hash,
+ islist = functionlist and true or false,
+ }
+
+ report("index %i, nx %i, ny %i, levels %i", nofcontours, nx, ny, nofvalues)
+end
+
+function mp.lmt_contours_stop()
+ local p = getparameterset()
+ local e = stoptiming("lmt_contours")
+ setcache(p)
+ p.result = nil
+ local f = p["function"]
+ local l = p.functions
+ report("index %i, %0.3f seconds for: %s",
+ nofcontours, e, "[ " .. concat(l or { f } ," ] [ ") .. " ]"
+ )
+end
+
+function mp.lmt_contours_bitmap_set()
+ local p = getparameterset()
+ local result = p.result
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local rawdata = result.data
+ local nx = result.nx
+ local ny = result.ny
+ local colors = result.colors
+ local depth = #colors[1] -- == 3 and "rgb" or "gray"
+
+ -- i need to figure out this offset of + 1
+
+ local bitmap = graphics.bitmaps.new(nx,ny,depth == 3 and "rgb" or "gray",1,false,true)
+
+ local palette = bitmap.index or { } -- has to start at 0
+ local data = bitmap.data
+ local p = 0
+
+ if depth == 3 then
+ for i=1,nofvalues do
+ local c = colors[i]
+ local r = round((c[1] or 0) * 255)
+ local g = round((c[2] or 0) * 255)
+ local b = round((c[3] or 0) * 255)
+ palette[p] = {
+ (r > 255 and 255) or (r < 0 and 0) or r,
+ (g > 255 and 255) or (g < 0 and 0) or g,
+ (b > 255 and 255) or (b < 0 and 0) or b,
+ }
+ p = p + 1
+ end
+ else
+ for i=1,nofvalues do
+ local s = colors[i][1]
+ local s = round((s or 0) * 255)
+ palette[p] = (
+ (s > 255 and 255) or (s < 0 and 0) or s
+ )
+ p = p + 1
+ end
+ end
+
+ -- As (1,1) is the left top corner so we need to flip of we start in
+ -- the left bottom (we cannot loop reverse because we want a properly
+ -- indexed table.
+
+ local k = 0
+ for y=ny,1,-1 do
+ k = k + 1
+ local d = data[k]
+ for x=1,nx do
+ d[x] = rawdata[x][y] - 1
+ end
+ end
+
+ result.bitmap = bitmap
+end
+
+function mp.lmt_contours_bitmap_get()
+ local p = getparameterset()
+ local result = p.result
+ local bitmap = result.bitmap
+ local box = nodes.hpack(graphics.bitmaps.flush(bitmap))
+ n_box = n_box + 1
+ nodes.boxes.savenode("contour",tostring(n_box),box)
+ return f_box(n_box,bitmap.xsize,bitmap.ysize)
+end
+
+function mp.lmt_contours_cleanup()
+ nodes.boxes.reset("contour")
+ n_box = 0
+end
+
+function mp.lmt_contours_edge_set()
+ local p = getparameterset()
+ local result = p.result
+
+ if result.cached then return end
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local data = result.data
+ local nx = result.nx
+ local ny = result.ny
+
+ local xmin = p.xmin
+ local xmax = p.xmax
+ local ymin = p.ymin
+ local ymax = p.ymax
+ local xstep = p.xstep
+ local ystep = p.ystep
+
+ local wsp = { }
+ local edges = { }
+
+ for value=1,nofvalues do
+
+ local iwsp = 0
+ local di = data[1]
+ local dc
+ local edge = { }
+ local e = 0
+ -- the next loop is fast
+ for i=1,nx do
+ local di1 = data[i+1]
+ local dij = di[1]
+ local d = dij - value
+ local dij1
+ for j=1,ny do
+ if j < ny then
+ dij1 = di[j+1]
+ dc = dij1 - value
+ if (d >= 0 and dc < 0) or (d < 0 and dc >= 0) then
+ iwsp = iwsp + 1
+ local y = (d * (j+1) - dc * j) / (d - dc)
+ if i == 1 then
+ wsp[iwsp] = { i, y, 0, (i + (j-1)*nx) }
+ elseif i == nx then
+ wsp[iwsp] = { i, y, (i - 1 + (j-1)*nx), 0 }
+ else
+ local jx = (i + (j-1)*nx)
+ wsp[iwsp] = { i, y, jx - 1, jx }
+ end
+ end
+ end
+ if i < nx then
+ local dc = di1[j] - value
+ if (d >= 0 and dc < 0) or (d < 0 and dc >= 0) then
+ iwsp = iwsp + 1
+ local x = (d * (i+1) - dc * i) / (d - dc)
+ if j == 1 then
+ wsp[iwsp] = { x, j, 0, (i + (j-1)*nx) }
+ elseif j == ny then
+ wsp[iwsp] = { x, j, (i + (j-2)*nx), 0 }
+ else
+ local jx = i + (j-1)*nx
+ wsp[iwsp] = { x, j, jx - nx, jx }
+ end
+ end
+ end
+ dij = dij1
+ d = dc
+ end
+ di = di1
+ end
+ -- the next loop takes time
+ for i=1,iwsp do
+ local wspi = wsp[i]
+ for isq=3,4 do
+ local nsq = wspi[isq]
+ if nsq ~= 0 then
+ local px = wspi[1]
+ local py = wspi[2]
+ local p = { px, py }
+ local pn = 2
+ wspi[isq] = 0
+ while true do
+ for ii=1,iwsp do
+ local w = wsp[ii]
+ local n1 = w[3]
+ local n2 = w[4]
+ if n1 == nsq then
+ local x = w[1]
+ local y = w[2]
+ if x ~= px or y ~= py then
+ pn = pn + 1
+ p[pn] = x
+ pn = pn + 1
+ p[pn] = y
+ px = x
+ py = y
+ end
+ nsq = n2
+ w[3] = 0
+ w[4] = 0
+ if nsq == 0 then
+ if pn == 1 then
+ pn = pn + 1
+ p[pn] = w
+ end
+ goto flush
+ end
+ elseif n2 == nsq then
+ local x = w[1]
+ local y = w[2]
+ if x ~= px or y ~= py then
+ pn = pn + 1
+ p[pn] = x
+ pn = pn + 1
+ p[pn] = y
+ px = x
+ py = y
+ end
+ nsq = n1
+ w[3] = 0
+ w[4] = 0
+ if nsq == 0 then
+ goto flush
+ end
+ end
+ end
+ end
+ ::flush::
+ e = e + 1
+ edge[e] = p
+ if mpflatten then
+ mpflatten(p)
+ end
+ end
+ end
+ end
+
+
+ edges[value] = edge
+
+ end
+
+ result.edges = edges
+
+end
+
+function mp.lmt_contours_shade_set(edgetoo)
+ local p = getparameterset()
+ local result = p.result
+
+ if result.cached then return end
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local data = result.data
+ local nx = result.nx
+ local ny = result.ny
+ local color = result.color
+
+ local edges = setmetatableindex("table")
+ local shades = setmetatableindex("table")
+
+ local sqtype = setmetatableindex("table")
+
+ local xspoly = { 0, 0, 0, 0, 0, 0 }
+ local yspoly = { 0, 0, 0, 0, 0, 0 }
+ local xrpoly = { }
+ local yrpoly = { }
+
+ local xrpoly = { } -- lua.newtable(2000,0)
+ local yrpoly = { } -- lua.newtable(2000,0)
+
+ -- for i=1,2000 do
+ -- xrpoly[i] = 0
+ -- yrpoly[i] = 0
+ -- end
+
+ -- Unlike a c compiler lua will not optimize loops to run in parallel so we expand
+ -- some of the loops and make sure we don't calculate when not needed. Not that nice
+ -- but not that bad either. Maybe I should just write this from scratch.
+
+-- local i = 0
+-- local j = 0
+
+ -- Analyze each rectangle separately. Overwrite lower colors
+
+ -- Unrolling the loops and copying code and using constants is faster and doesn't
+ -- produce much more code in the end, also because we then can leave out the not
+ -- seen branches. One can argue about the foundit2* blobs but by stepwise optimizing
+ -- this is the result.
+
+ shades[1] = { { 0, 0, nx - 1, 0, nx - 1, ny - 1, 0, ny - 1 } }
+ edges [1] = { { } }
+
+ -- this is way too slow ... i must have messed up some loop .. what is this with value 1
+
+ for value=1,nofvalues do
+-- for value=2,nofvalues do
+
+ local edge = { }
+ local nofe = 0
+ local shade = { }
+ local nofs = 0
+
+ for i=1,nx-1 do
+ local s = sqtype[i]
+ for j=1,ny-1 do
+ s[j] = 0
+ end
+ end
+
+ local nrp = 0
+
+ local function addedge(a,b,c,d)
+ nofe = nofe + 1 edge[nofe] = a
+ nofe = nofe + 1 edge[nofe] = b
+ nofe = nofe + 1 edge[nofe] = c
+ nofe = nofe + 1 edge[nofe] = d
+ end
+ while true do
+ -- search for a square of type 0 with >= 1 corner above contour level
+ local i
+ local j
+ local d0 = data[1]
+ local d1 = data[2]
+ for ii=1,nx do
+ local s = sqtype[ii]
+ for jj=1,ny do
+ if s[jj] == 0 then
+ if d0[jj] > value then i = ii j = jj goto foundit end
+ if d1[jj] > value then i = ii j = jj goto foundit end
+ local j1 = jj + 1
+ if d1[j1] > value then i = ii j = jj goto foundit end
+ if d0[j1] > value then i = ii j = jj goto foundit end
+ end
+ end
+ d0 = d1
+ d1 = data[ii+1]
+ end
+ break
+ ::foundit::
+ -- initialize r-polygon (nrp seems to be 1 or 2)
+ nrp = nrp + 1
+
+ local first = true
+ local nrpoly = 0
+ local nspoly = 0
+ local nrpm = -nrp
+ -- this is the main loop
+ while true do
+ -- search for a square of type -nrp
+ if first then
+ first = false
+ if sqtype[i][j] == 0 then -- true anyway
+ goto foundit1
+ end
+ end
+ for ii=1,nx do
+ local s = sqtype[ii]
+ for jj=1,ny do
+ if s[jj] == nrpm then
+ i = ii
+ j = jj
+ goto foundit1
+ end
+ end
+ end
+ break
+ ::foundit1::
+ while true do
+
+ -- search current then neighboring squares for square type 0, with a corner in common with current square above contour level
+
+ -- top/bottom ... a bit cheating here
+
+ local i_l, i_c, i_r -- i left current right
+ local j_b, j_c, j_t -- j bottom current top
+
+ local i_n = i + 1 -- i next (right)
+ local j_n = j + 1 -- j next (top)
+
+ local i_p = i - 1 -- i previous (bottom)
+ local j_p = j - 1 -- j previous (right)
+
+ local d_c = data[i]
+ local d_r = data[i_n]
+
+ local sq
+
+ i_c = i ; j_c = j ; if i_c < nx and j_c < ny then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_c[j_c] > value then i_l = i_p ; i_r = i_n ; j_b = j_p ; j_t = j_n ; goto foundit21 end
+ if d_c[j_n] > value then i_l = i_p ; i_r = i_n ; j_b = j_p ; j_t = j_n ; goto foundit22 end
+ if d_r[j_c] > value then i_l = i_p ; i_r = i_n ; j_b = j_p ; j_t = j_n ; goto foundit23 end
+ if d_r[j_n] > value then i_l = i_p ; i_r = i_n ; j_b = j_p ; j_t = j_n ; goto foundit24 end
+ end end
+
+ i_c = i_n ; j_c = j ; if i_c < nx and j_c < ny then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_r[j_c] > value then i_l = i ; i_r = i_n + 1 ; j_b = j_p ; j_t = j_n ; d_c = d_r ; d_r = data[i_r] ; goto foundit21 end
+ if d_r[j_n] > value then i_l = i ; i_r = i_n + 1 ; j_b = j_p ; j_t = j_n ; d_c = d_r ; d_r = data[i_r] ; goto foundit22 end
+ end end
+
+ i_c = i ; j_c = j_n ; if i_c < nx and j_c < ny then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_c[j_n] > value then i_l = i_p ; i_r = i_n ; j_b = j ; j_t = j_n + 1 ; goto foundit21 end
+ if d_r[j_n] > value then i_l = i_p ; i_r = i_n ; j_b = j ; j_t = j_n + 1 ; goto foundit23 end
+ end end
+
+ i_c = i_p ; j_c = j ; if i_c > 0 and j_c < ny then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_c[j_c] > value then i_l = i_p - 1 ; i_r = i ; j_b = j_p ; j_t = j_n ; d_r = d_c ; d_c = data[i_p] ; goto foundit23 end
+ if d_c[j_n] > value then i_l = i_p - 1 ; i_r = i ; j_b = j_p ; j_t = j_n ; d_r = d_c ; d_c = data[i_p] ; goto foundit24 end
+ end end
+
+ i_c = i ; j_c = j_p ; if i < nx and j_c > 0 then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_c[j] > value then i_l = i_p ; i_r = i_n ; j_b = j_p - 1 ; j_t = j ; goto foundit22 end
+ if d_r[j] > value then i_l = i_p ; i_r = i_n ; j_b = j_p - 1 ; j_t = j ; goto foundit24 end
+ end end
+
+ -- not found
+
+ sqtype[i][j] = nrp
+
+ break
+
+ -- define s-polygon for found square (i_c,j_c) - may have up to 6 sides
+
+ ::foundit21:: -- 1 2 3 4
+
+ sq[j_c] = nrpm
+
+ xspoly[1] = i_l ; yspoly[1] = j_b
+ xspoly[2] = i_c ; yspoly[2] = j_b
+ if d_r[j_c] > value then -- dd2
+ xspoly[3] = i_c ; yspoly[3] = j_c
+ if d_r[j_t] > value then -- dd3
+ xspoly[4] = i_l ; yspoly[4] = j_c
+ if d_c[j_t] > value then -- dd4
+ nspoly = 4
+ else
+ xspoly[5] = i_l ; yspoly[5] = j_c ; nspoly = 5
+ end
+ elseif d_c[j_t] > value then -- dd4
+ xspoly[4] = i_c ; yspoly[4] = j_c ;
+ xspoly[5] = i_l ; yspoly[5] = j_c ; nspoly = 5
+ else
+ xspoly[4] = i_l ; yspoly[4] = j_c ; nspoly = 4
+ if edgetoo then addedge(i_c, j_c, i_l, j_c) end
+ end
+ elseif d_r[j_t] > value then -- dd3
+ xspoly[3] = i_c ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_c
+ if d_c[j_t] > value then -- dd4
+ xspoly[5] = i_l ; yspoly[5] = j_c ; nspoly = 5
+ else
+ xspoly[5] = i_l ; yspoly[5] = j_c ;
+ xspoly[6] = i_l ; yspoly[6] = j_c ; nspoly = 6
+ end
+ elseif d_c[j_t] > value then -- dd4
+ if edgetoo then addedge(i_c, j_b, i_c, j_c) end
+ xspoly[3] = i_c ; yspoly[3] = j_c ;
+ xspoly[4] = i_l ; yspoly[4] = j_c ; nspoly = 4
+ else
+ if edgetoo then addedge(i_c, j_b, i_l, j_c) end
+ xspoly[3] = i_l ; yspoly[3] = j_c ; nspoly = 3
+ end
+ goto done
+
+ ::foundit22:: -- 4 1 2 3
+
+ sq[j_c] = nrpm
+
+ xspoly[1] = i_l ; yspoly[1] = j_c
+ xspoly[2] = i_l ; yspoly[2] = j_b
+ if d_c[j_c] > value then -- dd2
+ xspoly[3] = i_c ; yspoly[3] = j_b
+ if d_r[j_c] > value then -- dd3
+ xspoly[4] = i_c ; yspoly[4] = j_c
+ if d_r[j_t] > value then -- dd4
+ nspoly = 4
+ else
+ xspoly[5] = i_c ; yspoly[5] = j_c ; nspoly = 5 -- suspicious, the same
+ end
+ elseif d_r[j_t] > value then -- dd4
+ xspoly[4] = i_c ; yspoly[4] = j_b ;
+ xspoly[5] = i_c ; yspoly[5] = j_c ; nspoly = 5
+ else
+ if edgetoo then addedge(i_c, j_b, i_c, j_c) end
+ xspoly[4] = i_c ; yspoly[4] = j_c ; nspoly = 4
+ end
+ elseif d_r[j_c] > value then -- dd3
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_b
+ xspoly[5] = i_c ; yspoly[5] = j_c
+ if d_r[j_t] > value then -- dd4
+ nspoly = 5
+ else
+ xspoly[6] = i_c ; yspoly[6] = j_c ; nspoly = 6
+ end
+ elseif d_r[j_t] > value then -- dd4
+ if edgetoo then addedge(i_l, j_b, i_c, j_b) end
+ xspoly[3] = i_c ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_c ; nspoly = 4
+ else
+ if edgetoo then addedge(i_l, j_b, i_c, j_c) end
+ xspoly[3] = i_c ; yspoly[3] = j_c ; nspoly = 3
+ end
+ goto done
+
+ ::foundit23:: -- 2 3 4 1
+
+ sq[j_c] = nrpm
+
+ xspoly[1] = i_c ; yspoly[1] = j_b
+ xspoly[2] = i_c ; yspoly[2] = j_c
+ if d_r[j_t] > value then -- dd2
+ xspoly[3] = i_l ; yspoly[3] = j_c
+ if d_c[j_t] > value then -- dd3
+ xspoly[4] = i_l ; yspoly[4] = j_b
+ if d_c[j_c] > value then -- dd4
+ nspoly = 4
+ else
+ xspoly[5] = i_l ; yspoly[5] = j_b ; nspoly = 5
+ end
+ elseif d_c[j_c] > value then -- dd4
+ xspoly[4] = i_l ; yspoly[4] = j_c
+ xspoly[5] = i_l ; yspoly[5] = j_b ; nspoly = 5
+ else
+ if edgetoo then addedge(i_l, j_c, i_l, j_b) end
+ xspoly[4] = i_l ; yspoly[4] = j_b ; nspoly = 4
+ end
+ elseif d_c[j_t] > value then -- dd3
+ xspoly[3] = i_c ; yspoly[3] = j_c
+ xspoly[4] = i_l ; yspoly[4] = j_c
+ xspoly[5] = i_l ; yspoly[5] = j_b
+ if d_c[j_c] > value then -- dd4
+ nspoly = 5
+ else
+ xspoly[6] = i_l ; yspoly[6] = j_b ; nspoly = 6
+ end
+ elseif d_c[j_c] > value then -- dd4
+ if edgetoo then addedge(i_c, j_c, i_l, j_c) end
+ xspoly[3] = i_l ; yspoly[3] = j_c ;
+ xspoly[4] = i_l ; yspoly[4] = j_b ; nspoly = 4
+ else
+ if edgetoo then addedge(i_c, j_c, i_l, j_b) end
+ xspoly[3] = i_l ; yspoly[3] = j_b ; nspoly = 3
+ end
+ goto done
+
+ ::foundit24:: -- 3 4 1 2
+
+ sq[j_c] = nrpm
+
+ xspoly[1] = i_c ; yspoly[1] = j_c
+ xspoly[2] = i_l ; yspoly[2] = j_c
+ if d_c[j_t] > value then -- dd2
+ if d_c[j_c] > value then -- dd3
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_b
+ if d_r[j_c] > value then -- dd4
+ nspoly = 4
+ else
+ xspoly[5] = i_c ; yspoly[5] = j_b ; nspoly = 5
+ end
+ else
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ if d_r[j_c] > value then -- dd4
+
+ local xv34 = (dd3*i_c-dd4*i_l)/(dd3 - dd4) -- probably i_l
+ print("4.4 : xv34",xv34,i_c,i_l)
+
+ -- if edgetoo then addedge(i_l, j_b, xv34, j_b) end
+ xspoly[4] = xv34 ; yspoly[4] = j_b ;
+ xspoly[5] = i_c ; yspoly[5] = j_b ; nspoly = 5
+ else
+ if edgetoo then addedge(i_l, j_b, i_c, j_b) end
+ xspoly[4] = i_c ; yspoly[4] = j_b ; nspoly = 4
+ end
+ end
+ elseif d_c[j_c] > value then -- dd3
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ xspoly[4] = i_l ; yspoly[4] = j_b
+ xspoly[5] = i_c ; yspoly[5] = j_b
+ if d_r[j_c] > value then -- dd4
+ nspoly = 5
+ else
+ xspoly[6] = i_c ; yspoly[6] = j_b ; nspoly = 6
+ end
+ elseif d_r[j_c] > value then -- dd4
+ if edgetoo then addedge(i_l, j_c, i_l, j_b) end
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_b ; nspoly = 4
+ else
+ if edgetoo then addedge(i_l, j_c, i_c, j_b) end
+ xspoly[3] = i_c ; yspoly[3] = j_b ; nspoly = 3
+ end
+ -- goto done
+
+ ::done::
+ -- combine s-polygon with existing r-polygon, eliminating redundant segments
+
+ if nrpoly == 0 then
+ -- initiate r-polygon
+ for i=1,nspoly do
+ xrpoly[i] = xspoly[i]
+ yrpoly[i] = yspoly[i]
+ end
+ nrpoly = nspoly
+ else
+ -- search r-polygon and s-polygon for one side that matches
+ --
+ -- this is a bottleneck ... we keep this variant here but next go for a faster
+ -- alternative
+ --
+ -- local ispoly, irpoly
+ -- for r=nrpoly,1,-1 do
+ -- local r1
+ -- for s=1,nspoly do
+ -- local s1 = s % nspoly + 1
+ -- if xrpoly[r] == xspoly[s1] and yrpoly[r] == yspoly[s1] then
+ -- if not r1 then
+ -- r1 = r % nrpoly + 1
+ -- end
+ -- if xrpoly[r1] == xspoly[s] and yrpoly[r1] == yspoly[s] then
+ -- ispoly = s
+ -- irpoly = r
+ -- goto foundit3
+ -- end
+ -- end
+ -- end
+ -- end
+ --
+ -- local ispoly, irpoly
+ -- local xr1 = xrpoly[1]
+ -- local yr1 = yrpoly[1]
+ -- for r0=nrpoly,1,-1 do
+ -- for s0=1,nspoly do
+ -- if xr1 == xspoly[s0] and yr1 == yspoly[s0] then
+ -- if s0 == nspoly then
+ -- if xr0 == xspoly[1] and yr0 == yspoly[1] then
+ -- ispoly = s0
+ -- irpoly = r0
+ -- goto foundit3
+ -- end
+ -- else
+ -- local s1 = s0 + 1
+ -- if xr0 == xspoly[s1] and yr0 == yspoly[s1] then
+ -- ispoly = s0
+ -- irpoly = r0
+ -- goto foundit3
+ -- end
+ -- end
+ -- end
+ -- end
+ -- xr1 = xrpoly[r0]
+ -- yr1 = yrpoly[r0]
+ -- end
+ --
+ -- but ...
+ --
+ local minx = xspoly[1]
+ local miny = yspoly[1]
+ local maxx = xspoly[1]
+ local maxy = yspoly[1]
+ for i=1,nspoly do
+ local y = yspoly[i]
+ if y < miny then
+ miny = y
+ elseif y > maxy then
+ maxy = y
+ end
+ local x = xspoly[i]
+ if x < minx then
+ minx = y
+ elseif x > maxx then
+ maxx = x
+ end
+ end
+ -- we can delay accessing y ...
+ local ispoly, irpoly
+ local xr1 = xrpoly[1]
+ local yr1 = yrpoly[1]
+ for r0=nrpoly,1,-1 do
+ if xr1 >= minx and xr1 <= maxx and yr1 >= miny and yr1 <= maxy then
+ local xr0 = xrpoly[r0]
+ local yr0 = yrpoly[r0]
+ for s0=1,nspoly do
+ if xr1 == xspoly[s0] and yr1 == yspoly[s0] then
+ if s0 == nspoly then
+ if xr0 == xspoly[1] and yr0 == yspoly[1] then
+ ispoly = s0
+ irpoly = r0
+ goto foundit3
+ end
+ else
+ local s1 = s0 + 1
+ if xr0 == xspoly[s1] and yr0 == yspoly[s1] then
+ ispoly = s0
+ irpoly = r0
+ goto foundit3
+ end
+ end
+ end
+ end
+ xr1 = xr0
+ yr1 = yr0
+ else
+ xr1 = xrpoly[r0]
+ yr1 = yrpoly[r0]
+ end
+ end
+ --
+ goto nomatch3
+ ::foundit3::
+ local match1 = 0
+ local rpoly1 = irpoly + nrpoly
+ local spoly1 = ispoly - 1
+ for i=2,nspoly-1 do
+ -- search for further matches nearby
+ local ir = (rpoly1 - i) % nrpoly + 1
+ local is = (spoly1 + i) % nspoly + 1
+ if xrpoly[ir] == xspoly[is] and yrpoly[ir] == yspoly[is] then
+ match1 = match1 + 1
+ else
+ break -- goto nomatch1
+ end
+ end
+ ::nomatch1::
+ local match2 = 0
+ local rpoly2 = irpoly - 1
+ local spoly2 = ispoly + nspoly
+ for i=2,nspoly-1 do
+ -- search other way for further matches nearby
+ local ir = (rpoly2 + i) % nrpoly + 1
+ local is = (spoly2 - i) % nspoly + 1
+ if xrpoly[ir] == xspoly[is] and yrpoly[ir] == yspoly[is] then
+ match2 = match2 + 1
+ else
+ break -- goto nomatch2
+ end
+ end
+ ::nomatch2::
+ -- local dnrpoly = nspoly - 2 - 2*match1 - 2*match2
+ local dnrpoly = nspoly - 2*(match1 + match2 + 1)
+ local ispolystart = (ispoly + match1) % nspoly + 1 -- first node of s-polygon to include
+ local irpolyend = (rpoly1 - match1 - 1) % nrpoly + 1 -- last node of s-polygon to include
+ if dnrpoly ~= 0 then
+ local irpolystart = (irpoly + match2) % nrpoly + 1 -- first node of s-polygon to include
+ if irpolystart > irpolyend then
+ -- local ispolyend = (spoly1 - match2 + nspoly)%nspoly + 1 -- last node of s-polygon to include
+ if dnrpoly > 0 then
+ -- expand the arrays xrpoly and yrpoly
+ for i=nrpoly,irpolystart,-1 do
+ local k = i + dnrpoly
+ xrpoly[k] = xrpoly[i]
+ yrpoly[k] = yrpoly[i]
+ end
+ else -- if dnrpoly < 0 then
+ -- contract the arrays xrpoly and yrpoly
+ for i=irpolystart,nrpoly do
+ local k = i + dnrpoly
+ xrpoly[k] = xrpoly[i]
+ yrpoly[k] = yrpoly[i]
+ end
+ end
+ end
+ nrpoly = nrpoly + dnrpoly
+ end
+ if nrpoly < irpolyend then
+ for i=irpolyend,nrpoly+1,-1 do
+ -- otherwise these values get lost!
+ local k = i - nrpoly
+ xrpoly[k] = xrpoly[i]
+ yrpoly[k] = yrpoly[i]
+ end
+ end
+ local n = nspoly - 2 - match1 - match2
+ if n == 1 then
+ local irpoly1 = irpolyend % nrpoly + 1
+ local ispoly1 = ispolystart % nspoly + 1
+ xrpoly[irpoly1] = xspoly[ispoly1]
+ yrpoly[irpoly1] = yspoly[ispoly1]
+ elseif n > 0 then
+ -- often 2
+ for i=1,n do
+ local ii = i - 1
+ local ir = (irpolyend + ii) % nrpoly + 1
+ local is = (ispolystart + ii) % nspoly + 1
+ xrpoly[ir] = xspoly[is]
+ yrpoly[ir] = yspoly[is]
+ end
+ end
+ ::nomatch3::
+ end
+ end
+ end
+
+ if nrpoly > 0 then
+ local t = { }
+ local n = 0
+ for i=1,nrpoly do
+ n = n + 1 t[n] = xrpoly[i]
+ n = n + 1 t[n] = yrpoly[i]
+ end
+ if mpflatten then
+ mpflatten(t) -- maybe integrate
+ end
+ nofs = nofs + 1
+ shade[nofs] = t
+ -- print(value,nrpoly,#t,#t-nrpoly*2)
+ end
+
+ end
+
+ edges [value+1] = edge
+ shades[value+1] = shade
+-- edges [value] = edge
+-- shades[value] = shade
+ end
+
+ result.shades = shades
+ result.shapes = edges
+
+end
+
+-- accessors
+
+function mp.lmt_contours_nx (i) return getparameterset().result.nx end
+function mp.lmt_contours_ny (i) return getparameterset().result.ny end
+
+function mp.lmt_contours_nofvalues() return getparameterset().result.nofvalues end
+function mp.lmt_contours_value (i) return getparameterset().result.values[i] end
+
+function mp.lmt_contours_minz (i) return getparameterset().result.minz end
+function mp.lmt_contours_maxz (i) return getparameterset().result.maxz end
+
+function mp.lmt_contours_minmean (i) return getparameterset().result.minmean end
+function mp.lmt_contours_maxmean (i) return getparameterset().result.maxmean end
+
+function mp.lmt_contours_xrange () local p = getparameterset() mpstring(formatters["x = [%.3N,%.3N] ;"](p.xmin,p.xmax)) end
+function mp.lmt_contours_yrange () local p = getparameterset() mpstring(formatters["y = [%.3N,%.3N] ;"](p.ymin,p.ymax)) end
+
+function mp.lmt_contours_format()
+ local p = getparameterset()
+ return mpstring(p.result.islist and "@i" or p.zformat or p.format)
+end
+
+function mp.lmt_contours_function()
+ local p = getparameterset()
+ return mpstring(p.result.islist and concat(p["functions"], ", ") or p["function"])
+end
+
+function mp.lmt_contours_range()
+ local p = getparameterset()
+ local r = p.result.islist and p.range
+ if not r or #r == 0 then
+ return mpstring("")
+ elseif #r == 1 then
+ return mpstring(r[1])
+ else
+ return mpstring(formatters["z = [%s,%s]"](r[1],r[2]))
+ end
+end
+
+function mp.lmt_contours_edge_paths(value)
+ mpdraw(getparameterset().result.edges[value],true)
+ mpflush()
+end
+
+function mp.lmt_contours_shape_paths(value)
+ mpdraw(getparameterset().result.shapes[value],false)
+ mpflush()
+end
+
+function mp.lmt_contours_shade_paths(value)
+ mpfill(getparameterset().result.shades[value],true)
+ mpflush()
+end
+
+function mp.lmt_contours_color(value)
+ local p = getparameterset()
+ local color = p.result.colors[value]
+ if #color == 3 then
+ mptriplet(color)
+ else
+ return color[1]
+ end
+end
+
+-- The next code is based on the wikipedia page. It was a bit tedius job to define the
+-- coordinates but hupefully I made no errors. I rendered all shapes independently and
+-- tripple checked bit one never knows ...
+
+-- maybe some day write from scatch, like this (axis are swapped):
+
+local d = 1/2
+
+local paths = {
+ { 0, d, d, 0 },
+ { 1, d, d, 0 },
+ { 0, d, 1, d },
+ { 1, d, d, 1 },
+ { 0, d, d, 1, d, 0, 1, d }, -- saddle
+ { d, 0, d, 1 },
+ { 0, d, d, 1 },
+ { 0, d, d, 1 },
+ { d, 0, d, 1 },
+ { 0, d, d, 0, 1, d, d, 1 }, -- saddle
+ { 1, d, d, 1 },
+ { 0, d, 1, d },
+ { d, 0, 1, d },
+ { d, 0, 0, d },
+}
+
+local function whatever(data,nx,ny,threshold)
+ local edges = { }
+ local e = 0
+ local d0 = data[1]
+ for j=1,ny-1 do
+ local d1 = data[j+1]
+ local k = j + 1
+ for i=1,nx-1 do
+ local v = 0
+ local l = i + 1
+ local c1 = d0[i]
+ if c1 < threshold then
+ v = v + 8
+ end
+ local c2 = d0[l]
+ if c2 < threshold then
+ v = v + 4
+ end
+ local c3 = d1[l]
+ if c3 < threshold then
+ v = v + 2
+ end
+ local c4 = d1[i]
+ if c4 < threshold then
+ v = v + 1
+ end
+ if v > 0 and v < 15 then
+ if v == 5 or v == 10 then
+ local a = (c1 + c2 + c3 + c4) / 4
+ if a < threshold then
+ v = v == 5 and 10 or 5
+ end
+ local p = paths[v]
+ e = e + 1 edges[e] = k - p[2]
+ e = e + 1 edges[e] = i + p[1]
+ e = e + 1 edges[e] = k - p[4]
+ e = e + 1 edges[e] = i + p[3]
+ e = e + 1 edges[e] = k - p[6]
+ e = e + 1 edges[e] = i + p[5]
+ e = e + 1 edges[e] = k - p[8]
+ e = e + 1 edges[e] = i + p[7]
+ else
+ local p = paths[v]
+ e = e + 1 edges[e] = k - p[2]
+ e = e + 1 edges[e] = i + p[1]
+ e = e + 1 edges[e] = k - p[4]
+ e = e + 1 edges[e] = i + p[3]
+ end
+ end
+ end
+ d0 = d1
+ end
+ return edges
+end
+
+-- todo: just fetch when needed, no need to cache
+
+function mp.lmt_contours_edge_set_by_cell()
+ local p = getparameterset()
+ local result = p.result
+
+ if result.cached then return end
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local data = result.data
+ local nx = result.nx
+ local ny = result.ny
+ local lines = { }
+ result.lines = lines
+ for value=1,nofvalues do
+ lines[value] = whatever(data,ny,nx,value)
+ end
+end
+
+function mp.lmt_contours_edge_get_cell(value)
+ mpdraw(getparameterset().result.lines[value])
+ mpflush()
+end
+
+local singles = {
+ { d, 0, 0, 0, 0, d }, -- 1 0001
+ { d, 0, 0, d }, -- 2 0002
+ { 1, d, 1, 0, d, 0 }, -- 3 0010
+ { 1, d, 1, 0, 0, 0, 0, d }, -- 4 0011
+ { 1, d, 1, 0, d, 0, 0, d }, -- 5 0012
+ { 1, d, d, 0 }, -- 6 0020
+ { 1, d, d, 0, 0, 0, 0, d }, -- 7 0021
+ { 1, d, 0, d }, -- 8 0022
+ { d, 1, 1, 1, 1, d }, -- 9 0100
+ false, -- 10 0101
+ false, -- 11 0102
+ { d, 1, 1, 1, 1, 0, d, 0 }, -- 12 0110
+ { d, 1, 1, 1, 1, 0, 0, 0, 0, d }, -- 13 0111
+ { d, 1, 1, 1, 1, 0, d, 0, 0, d }, -- 14 0112
+ { d, 1, 1, 1, 1, d, d, 0 }, -- 15 0120
+ { d, 1, 1, 1, 1, d, d, 0, 0, 0, 0, d }, -- 16 0121
+ { d, 1, 1, 1, 1, d, 0, d }, -- 17 0122
+ { d, 1, 1, d }, -- 18 0200
+ false, -- 19 0201
+ false, -- 20 0202
+ { d, 1, 1, d, 1, 0, d, 0 }, -- 21 0210
+ { d, 1, 1, d, 1, 0, 0, 0, 0, d }, -- 22 0211
+ false, -- 23 0212
+ { d, 1, d, 0 }, -- 24 0220
+ { d, 1, d, 0, 0, 0, 0, d }, -- 25 0221
+ { d, 1, 0, d }, -- 26 0222
+ { 0, 1, d, 1, 0, d }, -- 27 1000
+ { 0, 1, d, 1, d, 0, 0, 0 }, -- 28 1001
+ { 0, 1, d, 1, d, 0, 0, d }, -- 29 1002
+ false, -- 30 1010
+ { 0, 1, d, 1, 1, d, 1, 0, 0, 0 }, -- 31 1011
+ { 0, 1, d, 1, 1, d, 1, 0, d, 0, 0, d }, -- 32 1012
+ false, -- 33 1020
+ { 0, 1, d, 1, 1, d, d, 0, 0, 0 }, -- 34 1021
+ { 0, 1, d, 1, 1, d, 0, d }, -- 35 1022
+ { 0, 1, 1, 1, 1, d, 0, d }, -- 36 1100
+ { 0, 1, 1, 1, 1, d, d, 0, 0, 0 }, -- 37 1101
+ { 0, 1, 1, 1, 1, d, d, 0, 0, d }, -- 38 1102
+ { 0, 1, 1, 1, 1, 0, d, 0, 0, d }, -- 39 1110
+ { 0, 1, 1, 1, 1, 0, 0, 0 }, -- 40 1111
+ { 0, 1, 1, 1, 1, 0, d, 0, 0, d }, -- 41 1112
+ { 0, 1, 1, 1, 1, d, d, 0, 0, d }, -- 42 1120
+ { 0, 1, 1, 1, 1, d, d, 0, 0, 0 }, -- 43 1121
+ { 0, 1, 1, 1, 1, d, 0, d }, -- 44 1122
+ { 0, 1, d, 1, 1, d, 0, d }, -- 45 1200
+ { 0, 1, d, 1, 1, d, d, 0, 0, 0 }, -- 46 1201
+ false, -- 47 1202
+ { 0, 1, d, 1, 1, d, 1, 0, d, 0, 0, d }, -- 48 1210
+ { 0, 1, d, 1, 1, d, 1, 0, 0, 0 }, -- 49 1211
+ false, -- 50 1212
+ { 0, 1, d, 1, d, 0, 0, d }, -- 51 1220
+ { 0, 1, d, 1, d, 0, 0, 0 }, -- 52 1221
+ { 0, 1, d, 1, 0, d }, -- 53 1222
+ { d, 1, 0, d }, -- 54 2000
+ { d, 1, d, 0, 0, 0, 0, d }, -- 55 2001
+ { d, 1, d, 0 }, -- 56 2002
+ false, -- 57 2010
+ { d, 1, 1, d, 1, 0, 0, 0, 0, d }, -- 58 2011
+ { d, 1, 1, d, 1, 0, d, 0 }, -- 59 2012
+ false, -- 60 2020
+ false, -- 61 2021
+ { d, 1, 1, d }, -- 62 2022
+ { d, 1, 1, 1, 1, d, 0, d }, -- 63 2100
+ { d, 1, 1, 1, 1, d, d, 0, 0, 0, 0, d }, -- 64 2101
+ { d, 1, 1, 1, 1, d, d, 0 }, -- 65 2102
+ { d, 1, 1, 1, 1, 0, d, 0, 0, d }, -- 66 2110
+ { d, 1, 1, 1, 1, 0, 0, 0, 0, d }, -- 67 2111
+ { d, 1, 1, 1, 1, 0, d, 0 }, -- 68 2112
+ false, -- 69 2120
+ false, -- 70 2121
+ { d, 1, 1, 1, 1, d }, -- 71 2122
+ { 1, d, 0, d }, -- 72 2200
+ { 1, d, d, 0, 0, 0, 0, d }, -- 73 2201
+ { 1, d, d, 0 }, -- 74 2202
+ { 1, d, 1, 0, d, 0, 0, d }, -- 75 2210
+ { 1, d, 1, 0, 0, 0, 0, d }, -- 76 2211
+ { 1, d, 1, 0, d, 0 }, -- 77 2212
+ { d, 0, 0, d }, -- 78 2220
+ { 0, d, 0, 0, d, 0 }, -- 79 2221
+}
+
+local sadles = {
+ false, false, false, false, false, false, false, false, false,
+ { { d, 1, 1, 1, 1, d }, { d, 0, 0, 0, 0, d }, { d, 1, 1, 1, 1, d, d, 0, 0, 0, 0, d }, false, false, false }, -- 10 0101
+ { { d, 1, 1, 1, 1, d }, { d, 0, 0, d }, { d, 1, 1, 1, 1, d, d, 0, 0, d }, false, false, false }, -- 11 0102
+ false, false, false, false, false, false, false,
+ { { d, 1, 1, d }, { d, 0, 0, 0, 0, d }, { d, 1, 1, d, d, 0, 0, 0, 0, d }, false, false, false }, -- 19 0201
+ { { d, 1, 1, d }, { d, 0, 0, d }, { d, 1, 1, d, d, 0, 0, d }, false, { d, 1, 0, d }, { 1, d, d, 0 } }, -- 20 0202
+ false, false,
+ { false, false, { d, 1, 1, d, 1, 0, d, 0, 0, d }, false, { d, 1, 0,d, }, { 1, d, 1, 0,d, 0 } }, -- 23 0212
+ false, false, false, false, false, false,
+ { { 0, 1, d, 1, 0, d }, { 1, d, 1, 0, d, 0 }, { 0, 1, d, 1, 1, d, 1, 0, d, 0, 0, d }, false, false, false }, -- 30 1010
+ false, false,
+ { { 1, 0, d, 0, 0, d, }, { 1, d, d, 0 }, { 0, 1, d, 1, 1, d, d, 0, 0, d }, false, false, false }, -- 33 1020
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ { false, false, { 0,1, d, 1, 1, d, d, 0, 0, d }, false, { 0,1, d, 1, 0, d }, {1, d, d, 0 } }, -- 47 1202
+ false, false,
+ { false, false, { 0, 1, d, 1, 1, d, 1, 0, d, 0, 0, d }, false, { 0, 1, d, 1, 0, d }, { 1, d, 1, 0, d, 0 } }, -- 50 1212
+ false, false, false, false, false, false,
+ { { d, 1, 0, d }, { 1, d, 1, 0, 0, d }, { d, 1, 1, d, 1, 0, d, 0, 0, d }, false, false, false }, -- 57 2010
+ false, false,
+ { { d, 1, 0,d }, { 1, d, d, 0 }, { d, 1, 1, d, d, 0, 0, d }, false, { d, 1, 1, d }, { d, 0, 0, d } }, -- 60 2020
+ { false, false, { d, 1, 1, d, d, 0, 0, 0, 0, d }, false, { d, 1, 1, d }, { d, 0, 0, 0, 0, d } }, -- 61 2021
+ false, false, false, false, false, false, false,
+ { false, false, { d, 1, 1, 1, 1, d, d, 0, 0, d }, false, { d, 1, 1, 1, 1, d }, { d, 0,0,d } }, -- 69 2120
+ { false, false, { d, 1, 1, 1, 1, d, d, 0, 0, 0, 0, d }, false, { d, 1, 1, 1, 1, d }, { d, 0, 0, 0, 0, d } }, -- 70 2121
+}
+
+local function whatever(data,nx,ny,threshold,background)
+
+ if background then
+
+ local llx = 1/2
+ local lly = llx
+ local urx = ny + llx
+ local ury = nx + lly
+
+ return { { llx, lly, urx, 0, urx, ury, 0, ury } }
+
+ else
+
+ local bands = { }
+ local b = 0
+
+ local function band(s,n,x,y) -- simple. no closure so fast
+ if n == 6 then
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3], x - s[ 6], y + s[ 5],
+ }
+ elseif n == 8 then
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3], x - s[ 6], y + s[ 5],
+ x - s[ 8], y + s[ 7],
+ }
+ elseif n == 10 then
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3], x - s[ 6], y + s[ 5],
+ x - s[ 8], y + s[ 7], x - s[10], y + s[ 9],
+ }
+ elseif n == 4 then
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3],
+ }
+ else -- 12
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3], x - s[ 6], y + s[ 5],
+ x - s[ 8], y + s[ 7], x - s[10], y + s[ 9], x - s[12], y + s[11],
+ }
+ end
+ end
+
+ local pp = { }
+
+ local d0 = data[1]
+ for j=1,ny-1 do
+ local d1 = data[j+1]
+ local k = j + 1
+ local p = false
+ for i=1,nx-1 do
+ local v = 0
+ local l = i + 1
+ local c1 = d0[i]
+ if c1 == threshold then
+ v = v + 27
+ elseif c1 > threshold then
+ v = v + 54
+ end
+ local c2 = d0[l]
+ if c2 == threshold then
+ v = v + 9
+ elseif c2 > threshold then
+ v = v + 18
+ end
+ local c3 = d1[l]
+ if c3 == threshold then
+ v = v + 3
+ elseif c3 > threshold then
+ v = v + 6
+ end
+ local c4 = d1[i]
+ if c4 == threshold then
+ v = v + 1
+ elseif c4 > threshold then
+ v = v + 2
+ end
+ if v > 0 and v < 80 then
+ if v == 40 then
+ -- a little optimization: full areas appended horizontally
+ if p then
+ p[4] = l -- i + 1
+ p[6] = l -- i + 1
+ else
+ -- x-0 y+1 x-1 y+1 x-1 y+0 x-0 y+0
+ p = { j, i, j, l, k, l, k, i }
+ b = b + 1 ; bands[b] = p
+ end
+ else
+ local s = singles[v]
+ if s then
+ b = b + 1 ; bands[b] = band(s,#s,k,i)
+ else
+ local s = sadles[v]
+ if s then
+ local m = (c1 + c2 + c3 + c4) / 4
+ if m < threshold then
+ local s1 = s[1] if s1 then b = b + 1 ; bands[b] = band(s1,#s1,i,j) end
+ local s2 = s[2] if s2 then b = b + 1 ; bands[b] = band(s2,#s2,i,j) end
+ elseif m == threshold then
+ local s3 = s[3] if s3 then b = b + 1 ; bands[b] = band(s3,#s3,i,j) end
+ local s4 = s[4] if s4 then b = b + 1 ; bands[b] = band(s4,#s4,i,j) end
+ else
+ local s5 = s[5] if s5 then b = b + 1 ; bands[b] = band(s5,#s5,i,j) end
+ local s6 = s[6] if s6 then b = b + 1 ; bands[b] = band(s6,#s6,i,j) end
+ end
+ end
+ end
+ p = false
+ end
+ else
+ p = false
+ end
+ end
+ d0 = d1
+ end
+ return bands
+ end
+end
+
+function mp.lmt_contours_edge_set_by_band(value)
+ local p = getparameterset()
+ local result = p.result
+
+ if result.cached then return end
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local data = result.data
+ local nx = result.nx
+ local ny = result.ny
+ local bands = { }
+ result.bands = bands
+ for value=1,nofvalues do
+ bands[value] = whatever(data,ny,nx,value,value == 1)
+ end
+end
+
+function mp.lmt_contours_edge_get_band(value)
+ mpfill(getparameterset().result.bands[value],true)
+ mpflush()
+end
diff --git a/tex/context/base/mkiv/mlib-ctx.lua b/tex/context/base/mkiv/mlib-ctx.lua
index e19f111b4..88ef98f58 100644
--- a/tex/context/base/mkiv/mlib-ctx.lua
+++ b/tex/context/base/mkiv/mlib-ctx.lua
@@ -332,7 +332,7 @@ implement {
}
statistics.register("metapost", function()
- local n = metapost.n
+ local n = metapost.nofruns
if n and n > 0 then
local elapsedtime = statistics.elapsedtime
local elapsed = statistics.elapsed
diff --git a/tex/context/base/mkiv/mlib-ctx.mkiv b/tex/context/base/mkiv/mlib-ctx.mkiv
index 78a26ad1c..c90536937 100644
--- a/tex/context/base/mkiv/mlib-ctx.mkiv
+++ b/tex/context/base/mkiv/mlib-ctx.mkiv
@@ -19,7 +19,8 @@
\registerctxluafile{mlib-lmp}{}
\registerctxluafile{mlib-int}{}
-\doifelsefileexists{mlib-cnt.lua}{\registerctxluafile{mlib-int}{}}{}
+\doifelsefileexists{mlib-cnt.lua}{\registerctxluafile{mlib-cnt}{}}{}
+\doifelsefileexists{mlib-svg.lua}{\registerctxluafile{mlib-svg}{}}{}
\unprotect
diff --git a/tex/context/base/mkiv/mlib-ctx.mkxl b/tex/context/base/mkiv/mlib-ctx.mkxl
index c66e6f968..bb2460627 100644
--- a/tex/context/base/mkiv/mlib-ctx.mkxl
+++ b/tex/context/base/mkiv/mlib-ctx.mkxl
@@ -22,6 +22,9 @@
\registerctxluafile{mlib-int}{}
\registerctxluafile{mlib-lmt}{}
+\doifelsefileexists{mlib-cnt.lua}{\registerctxluafile{mlib-cnt}{}}{}
+\doifelsefileexists{mlib-svg.lua}{\registerctxluafile{mlib-svg}{}}{}
+
\unprotect
\protect \endinput
diff --git a/tex/context/base/mkiv/mlib-lmp.lua b/tex/context/base/mkiv/mlib-lmp.lua
index 93a758681..22607be8d 100644
--- a/tex/context/base/mkiv/mlib-lmp.lua
+++ b/tex/context/base/mkiv/mlib-lmp.lua
@@ -134,3 +134,11 @@ do
end
end
+
+function mp.lmt_svg_include()
+ local name = metapost.getparameter { "svg", "filename" }
+ local mps = metapost.svgtomp {
+ data = name and name ~= "" and io.loaddata(name) or "",
+ }
+ mp.direct(mps)
+end
diff --git a/tex/context/base/mkiv/mlib-lua.lua b/tex/context/base/mkiv/mlib-lua.lua
index 9e3802396..29ef334f1 100644
--- a/tex/context/base/mkiv/mlib-lua.lua
+++ b/tex/context/base/mkiv/mlib-lua.lua
@@ -75,6 +75,7 @@ do
local scan_cmykcolor = mplib.scan_cmykcolor
local scan_transform = mplib.scan_transform
local scan_path = mplib.scan_path
+ local scan_pen = mplib.scan_pen
scan.next = function(k) return scan_next (currentmpx,k) end
scan.expression = function(k) return scan_expression(currentmpx,k) end
@@ -90,6 +91,7 @@ do
scan.cmykcolor = function(t) return scan_cmykcolor (currentmpx,t) end
scan.transform = function(t) return scan_transform (currentmpx,t) end
scan.path = function(t) return scan_path (currentmpx,t) end
+ scan.pen = function(t) return scan_pen (currentmpx,t) end
else
@@ -148,6 +150,7 @@ do
local f_triplet = formatters["(%F,%F,%F)"]
local f_quadruple = formatters["(%F,%F,%F,%F)"]
local f_transform = formatters["totransform(%F,%F,%F,%F,%F,%F)"]
+ local f_pen = formatters["(pencircle transformed totransform(%F,%F,%F,%F,%F,%F))"]
local f_points = formatters["%p"]
local f_pair_pt = formatters["(%p,%p)"]
@@ -516,7 +519,12 @@ do
local tn = #t
if tn == 1 then
local t1 = t[1]
- n = n + 1 ; buffer[n] = f2(t1[1],t1[2])
+ n = n + 1
+ if t.pen then
+ buffer[n] = f_pen(unpack(t1))
+ else
+ buffer[n] = f2(t1[1],t1[2])
+ end
elseif tn > 0 then
if connector == true or connector == nil then
connector = ".."
@@ -534,6 +542,8 @@ do
local a = t[1]
local b = t[2]
n = n + 1
+ buffer[n] = "("
+ n = n + 1
if six and #a == 6 and #b == 6 then
buffer[n] = f6(a[1],a[2],a[5],a[6],b[3],b[4])
controls = ".."
@@ -575,6 +585,8 @@ do
else
buffer[n] = f2(a[1],a[2])
end
+ n = n + 1
+ buffer[n] = ")"
end
end
end
diff --git a/tex/context/base/mkiv/mlib-pdf.lua b/tex/context/base/mkiv/mlib-pdf.lua
index 524e930a9..ece332d84 100644
--- a/tex/context/base/mkiv/mlib-pdf.lua
+++ b/tex/context/base/mkiv/mlib-pdf.lua
@@ -400,7 +400,7 @@ function metapost.flush(specification,result)
local linecap = -1
local linejoin = -1
local dashed = false
-local linewidth = false
+ local linewidth = false
local llx = properties.llx
local lly = properties.lly
local urx = properties.urx
@@ -533,10 +533,10 @@ local linewidth = false
if pen then
if pen.type == "elliptical" then
transformed, penwidth = pen_characteristics(original) -- boolean, value
-if penwidth ~= linewidth then
- result[#result+1] = f_w(penwidth) -- todo: only if changed
- linewidth = penwidth
-end
+ if penwidth ~= linewidth then
+ result[#result+1] = f_w(penwidth)
+ linewidth = penwidth
+ end
if objecttype == "fill" then
objecttype = "both"
end
@@ -624,8 +624,7 @@ end
end
if object.grouped then
-- can be qQ'd so changes can end up in groups
- miterlimit, linecap, linejoin, dashed = -1, -1, -1, "" -- was false
-linewidth = false
+ miterlimit, linecap, linejoin, dashed, linewidth = -1, -1, -1, "", false
end
end
end
diff --git a/tex/context/base/mkiv/mlib-pps.lua b/tex/context/base/mkiv/mlib-pps.lua
index 1457020ef..46d436466 100644
--- a/tex/context/base/mkiv/mlib-pps.lua
+++ b/tex/context/base/mkiv/mlib-pps.lua
@@ -691,7 +691,7 @@ metapost.splitprescript = splitprescript
-- end
function metapost.pluginactions(what,t,flushfigure) -- before/after object, depending on what
- if top.plugmode then -- hm, what about other features
+ if top and top.plugmode then -- hm, what about other features
for i=1,#what do
local wi = what[i]
if type(wi) == "function" then
@@ -708,14 +708,14 @@ function metapost.pluginactions(what,t,flushfigure) -- before/after object, depe
end
function metapost.resetplugins(t) -- intialize plugins, before figure
- if top.plugmode then
+ if top and top.plugmode then
outercolormodel = colors.currentmodel() -- currently overloads the one set at the tex end
resetteractions.runner(t)
end
end
function metapost.processplugins(object) -- each object (second pass)
- if top.plugmode then
+ if top and top.plugmode then
local prescript = object.prescript -- specifications
if prescript and #prescript > 0 then
local before = { }
diff --git a/tex/context/base/mkiv/mlib-run.lua b/tex/context/base/mkiv/mlib-run.lua
index fb1367151..bf7c8a796 100644
--- a/tex/context/base/mkiv/mlib-run.lua
+++ b/tex/context/base/mkiv/mlib-run.lua
@@ -57,6 +57,7 @@ metapost.showlog = false
metapost.lastlog = ""
metapost.texerrors = false
metapost.exectime = metapost.exectime or { } -- hack
+metapost.nofruns = 0
local mpxformats = { }
local mpxterminals = { }
@@ -100,6 +101,7 @@ local function executempx(mpx,data)
elseif type(data) == "table" then
data = prepareddata(data,collapse)
end
+ metapost.nofruns = metapost.nofruns + 1
return mpx:execute(data)
end
@@ -822,70 +824,13 @@ function metapost.directrun(formatname,filename,outputformat,astable,mpdata)
report_metapost("producing postscript and svg is no longer supported")
end
--- goodie
-
-function metapost.quickanddirty(mpxformat,data,incontext)
- if not data then
- mpxformat = "metafun"
- data = mpxformat
- end
- local code, bbox
- local flusher = {
- startfigure = function(n,llx,lly,urx,ury)
- code = { }
- bbox = { llx, lly, urx, ury }
- end,
- flushfigure = function(t)
- for i=1,#t do
- code[#code+1] = t[i]
- end
- end,
- stopfigure = function()
- end
- }
- local data = formatters["; beginfig(1) ;\n %s\n ; endfig ;"](data)
- metapost.process {
- mpx = mpxformat,
- flusher = flusher,
- askedfig = "all",
- useplugins = incontext,
- incontext = incontext,
- data = { data },
- }
- if code then
- return {
- bbox = bbox or { 0, 0, 0, 0 },
- code = code,
- data = data,
- }
- else
- report_metapost("invalid quick and dirty run")
- end
-end
-
-function metapost.getstatistics(memonly)
- if memonly then
- local n, m = 0, 0
- for name, mpx in next, mpxformats do
- n = n + 1
- m = m + mpx:statistics().memory
- end
- return n, m
- else
- local t = { }
- for name, mpx in next, mpxformats do
- t[name] = mpx:statistics()
- end
- return t
- end
-end
-
do
local result = { }
local width = 0
local height = 0
local depth = 0
+ local bbox = { 0, 0, 0, 0 }
local flusher = {
startfigure = function(n,llx,lly,urx,ury)
@@ -893,35 +838,59 @@ do
width = urx - llx
height = ury
depth = -lly
+ bbox = { llx, lly, urx, ury }
end,
flushfigure = function(t)
+ local r = #result
for i=1,#t do
- result[#result+1] = t[i]
+ r = r + 1
+ result[r] = t[i]
end
end,
stopfigure = function()
- end
+ end,
}
- function metapost.simple(format,code) -- even less than metapost.quickcanddirty
- local mpx = metapost.pushformat { } -- takes defaults
- -- metapost.setoutercolor(2)
+ function metapost.simple(format,code,useextensions)
+ local mpx = metapost.pushformat {
+ instance = "simplefun",
+ format = "metafun",
+ method = "double",
+ }
metapost.process {
mpx = mpx,
flusher = flusher,
askedfig = 1,
- useplugins = false,
- incontext = false,
+ useplugins = useextensions,
data = { "beginfig(1);", code, "endfig;" },
+ incontext = false,
}
metapost.popformat()
if result then
local stream = concat(result," ")
- result = nil -- cleanup
- return stream, width, height, depth
+ result = { } -- nil -- cleanup .. weird, we can have a dangling q
+ return stream, width, height, depth, bbox
else
- return "", 0, 0, 0
+ return "", 0, 0, 0, { 0, 0, 0, 0 }
end
end
end
+
+function metapost.getstatistics(memonly)
+ if memonly then
+ local n, m = 0, 0
+ for name, mpx in next, mpxformats do
+ n = n + 1
+ m = m + mpx:statistics().memory
+ end
+ return n, m
+ else
+ local t = { }
+ for name, mpx in next, mpxformats do
+ t[name] = mpx:statistics()
+ end
+ return t
+ end
+end
+
diff --git a/tex/context/base/mkiv/mlib-scn.lua b/tex/context/base/mkiv/mlib-scn.lua
index a85c861bd..8867455b6 100644
--- a/tex/context/base/mkiv/mlib-scn.lua
+++ b/tex/context/base/mkiv/mlib-scn.lua
@@ -60,6 +60,7 @@ local scancolor = scanners.color
local scancmykcolor = scanners.cmykcolor
local scantransform = scanners.transform
local scanpath = scanners.path
+local scanpen = scanners.pen
local mpprint = mp.print
local mpnumeric = mp.numeric
@@ -164,6 +165,7 @@ typescanners = {
[types.cmykcolor] = scan_cmykcolor,
[types.transform] = scan_transform,
[types.path] = scanpath,
+ [types.pen] = scanpen,
}
table.setmetatableindex(tokenscanners,function()
@@ -603,6 +605,22 @@ local function getparameterpath()
end
end
+local function getparameterpen()
+ local list, n = collectnames()
+ local v = namespaces
+ for i=1,n do
+ v = v[list[i]]
+ if not v then
+ break
+ end
+ end
+ if type(v) == "table" then
+ return mppath(v,"..",true)
+ else
+ return mppair(0,0)
+ end
+end
+
local function getparametertext()
local list, n = collectnames()
local strut = list[n]
@@ -660,6 +678,7 @@ metapost.registerscript("getparameterdefault", getparameterdefault)
metapost.registerscript("getparametercount", getparametercount)
metapost.registerscript("getmaxparametercount",getmaxparametercount)
metapost.registerscript("getparameterpath", getparameterpath)
+metapost.registerscript("getparameterpen", getparameterpen)
metapost.registerscript("getparametertext", getparametertext)
--------.registerscript("getparameteroption", getparameteroption)
metapost.registerscript("pushparameters", pushparameters)
diff --git a/tex/context/base/mkiv/mlib-svg.lua b/tex/context/base/mkiv/mlib-svg.lua
new file mode 100644
index 000000000..c3635480d
--- /dev/null
+++ b/tex/context/base/mkiv/mlib-svg.lua
@@ -0,0 +1,1635 @@
+if not modules then modules = { } end modules ['mlib-svg'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- As usual with these standards, things like a path can be very compact while the rest is
+-- very verbose which defeats the point. This is a first attempt. There will be a converter
+-- to MP as well as directly to PDF. This module was made for one of the dangerous curves
+-- talks at the 2019 CTX meeting. I will do the font when I need it (not that hard).
+--
+-- There is no real need to boost performance here .. we can always make a fast variant
+-- when really needed. I will also do some of the todo's when I run into proper fonts.
+
+-- Written with Anne Clark on speakers as distraction.
+
+-- TODO:
+
+-- optimize
+-- test for gzip header 0x1F 0x8B 0x08
+-- var()
+-- color hash
+-- currentColor
+-- instances
+-- --color<decimal>
+-- glyph<id>
+-- shading
+-- "none" -> false
+-- clip = [ auto | rect(llx,lly,urx,ury) ] (in svg)
+-- xlink url ... whatever
+-- mp svg module + shortcuts
+-- withpen -> pickup
+
+-- The fact that in the more recent versions of SVG the older text related elements
+-- are depricated and not even supposed to be supported, combined with the fact that
+-- the text element assumes css styling, demonstrates that there is not so much as a
+-- standard. It basically means that whatever technology dominates at some point
+-- (probably combined with some libraries that at that point exist) determine what
+-- is standard. Anyway, it probably also means that these formats are not that
+-- suitable for long term archival purposes. So don't take the next implementation
+-- too serious.
+
+-- We can do a direct conversion to PDF but then we also loose the abstraction which
+-- in the future will be used.
+
+local type, tonumber = type, tonumber
+
+local P, S, R, C, Ct, Cs, Cc, Cp, Carg = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Ct, lpeg.Cs, lpeg.Cc, lpeg.Cp, lpeg.Carg
+
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+local pi, sin, cos, asin, sind, cosd, tan, abs, sqrt = math.pi, math.sin, math.cos, math.asin, math.sind, math.cosd, math.tan, math.abs, math.sqrt
+local concat, setmetatableindex = table.concat, table.setmetatableindex
+local gmatch, gsub, find, match, rep = string.gmatch, string.gsub, string.find, string.match, string.rep
+local formatters = string.formatters
+
+local xmlconvert, xmlcollected, xmlcount, xmlfirst, xmlroot, xmltext = xml.convert, xml.collected, xml.count, xml.first, xml.root, xml.text
+
+metapost = metapost or { }
+local metapost = metapost
+local context = context
+
+local report = logs.reporter("metapost","svg")
+
+local trace = false
+-- local trace = true
+
+-- todo: also a high res mode
+-- todo: optimize (no hurry)
+
+local f_rectangle = formatters['unitsquare xyscaled (%.3N,%.3N) shifted (%.3N,%.3N)']
+local f_rounded = formatters['roundedsquarexy(%.3N,%.3N,%.3N,%.3N) shifted (%.3N,%.3N)']
+local f_ellipse = formatters['(fullcircle xyscaled (%.3N,%.3N) shifted (%.3N,%.3N))']
+local f_circle = formatters['(fullcircle scaled %.3N shifted (%.3N,%.3N))']
+local f_line = formatters['((%.3N,%.3N)--(%.3N,%.3N))']
+local f_fill = formatters['fill %s(%s--cycle)%s%s%s ;'] -- play safe
+local f_fill_cycle_c = formatters['fill %s(%s..cycle)%s%s%s ;']
+local f_fill_cycle_l = formatters['fill %s(%s--cycle)%s%s%s ;']
+local f_eofill = formatters['eofill %s(%s--cycle)%s%s%s ;'] -- play safe
+local f_eofill_cycle_c = formatters['eofill %s(%s..cycle)%s%s%s ;']
+local f_eofill_cycle_l = formatters['eofill %s(%s--cycle)%s%s%s ;']
+local f_nofill = formatters['nofill %s(%s--cycle)%s ;'] -- play safe
+local f_nofill_cycle_c = formatters['nofill %s(%s..cycle)%s ;']
+local f_nofill_cycle_l = formatters['nofill %s(%s--cycle)%s ;']
+local f_draw = formatters['draw %s(%s)%s%s%s%s%s ;']
+local f_nodraw = formatters['nodraw %s(%s)%s ;']
+
+-- local f_fill = formatters['F %s(%s--C)%s%s%s ;'] -- play safe
+-- local f_fill_cycle_c = formatters['F %s(%s..C)%s%s%s ;']
+-- local f_fill_cycle_l = formatters['F %s(%s--C)%s%s%s ;']
+-- local f_eofill = formatters['E %s(%s--C)%s%s%s ;'] -- play safe
+-- local f_eofill_cycle_c = formatters['E %s(%s..C)%s%s%s ;']
+-- local f_eofill_cycle_l = formatters['E %s(%s--C)%s%s%s ;']
+-- local f_nofill = formatters['f %s(%s--C)%s ;'] -- play safe
+-- local f_nofill_cycle_c = formatters['f %s(%s..C)%s ;']
+-- local f_nofill_cycle_l = formatters['f %s(%s--C)%s ;']
+-- local f_draw = formatters['D %s(%s)%s%s%s%s%s ;']
+-- local f_nodraw = formatters['d %s(%s)%s ;']
+
+local f_color = formatters[' withcolor "%s"']
+local f_rgb = formatters[' withcolor (%.3N,%.3N,%.3N)']
+local f_rgba = formatters[' withcolor (%.3N,%.3N,%.3N) withtransparency (1,%3N)']
+local f_triplet = formatters['(%.3N,%.3N,%.3N)']
+local f_gray = formatters[' withcolor %.3N']
+local f_opacity = formatters[' withtransparency (1,%.3N)']
+local f_pen = formatters[' withpen pencircle scaled %.3N']
+
+local f_dashed_n = formatters[" dashed dashpattern (%s ) "]
+local f_dashed_y = formatters[" dashed dashpattern (%s ) shifted (%.3N,0) "]
+
+local f_moveto = formatters['(%N,%n)']
+local f_curveto_z = formatters[' controls (%.3N,%.3N) and (%.3N,%.3N) .. (%.3N,%.3N)']
+local f_curveto_n = formatters['.. controls (%.3N,%.3N) and (%.3N,%.3N) .. (%.3N,%.3N)']
+local f_lineto_z = formatters['(%.3N,%.3N)']
+local f_lineto_n = formatters['-- (%.3N,%.3N)']
+
+local f_rotatedaround = formatters[" ) rotatedaround((%.3N,%.3N),%.3N)"]
+local f_rotated = formatters[" ) rotated(%.3N)"]
+local f_shifted = formatters[" ) shifted(%.3N,%.3N)"]
+local f_slanted_x = formatters[" ) xslanted(%.3N)"]
+local f_slanted_y = formatters[" ) yslanted(%.3N)"]
+local f_scaled = formatters[" ) scaled(%.3N)"]
+local f_xyscaled = formatters[" ) xyscaled(%.3N,%.3N)"]
+local f_matrix = formatters[" ) transformed bymatrix(%.3N,%.3N,%.3N,%.3N,%.3N,%.3N)"]
+
+-- penciled n -> withpen pencircle scaled n
+-- applied (...) -> transformed bymatrix (...)
+-- withopacity n -> withtransparency (1,n)
+
+local s_clip_start = 'draw image ('
+local f_clip_stop = formatters[') ; clip currentpicture to (%s) ;']
+local f_eoclip_stop = formatters[') ; eoclip currentpicture to (%s) ;']
+
+local f_transform_start = formatters["draw %s image ( "]
+local f_transform_stop = formatters[") %s ;"]
+
+local s_offset_start = "draw image ( "
+local f_offset_stop = formatters[") shifted (%.3N,%.3N) ;"]
+
+local f_viewport_start = "draw image ("
+local f_viewport_stop = ") ;"
+local f_viewport_shift = formatters["currentpicture := currentpicture shifted (%03N,%03N);"]
+local f_viewport_clip = formatters["clip currentpicture to (unitsquare xyscaled (%03N,%03N));"]
+
+local f_linecap = formatters["interim linecap := %s ;"]
+local f_linejoin = formatters["interim linejoin := %s ;"]
+local f_miterlimit = formatters["interim miterlimit := %s ;"]
+
+local s_begingroup = "begingroup;"
+local s_endgroup = "endgroup;"
+
+-- make dedicated macro
+
+local s_shade_linear = ' withshademethod "linear" '
+local s_shade_circular = ' withshademethod "circular" '
+local f_shade_step = formatters['withshadestep ( withshadefraction %.3N withshadecolors(%s,%s) )']
+local f_shade_one = formatters['withprescript "sh_center_a=%.3N %.3N"']
+local f_shade_two = formatters['withprescript "sh_center_b=%.3N %.3N"']
+
+local f_text_scaled = formatters['(textext.drt("%s") scaled %.3N shifted (%.3N,%.3N))']
+local f_text_normal = formatters['(textext.drt("%s") shifted (%.3N,%.3N))']
+
+local p_digit = lpegpatterns.digit
+local p_hexdigit = lpegpatterns.hexdigit
+local p_space = lpegpatterns.whitespace
+
+local p_hexcolor = P("#") * C(p_hexdigit*p_hexdigit)^1 / function(r,g,b)
+ r = tonumber(r,16)/255
+ if g then
+ g = tonumber(g,16)/255
+ end
+ if b then
+ b = tonumber(b,16)/255
+ end
+ if b then
+ return f_rgb(r,g,b)
+ else
+ return f_gray(r)
+ end
+end
+
+local p_hexcolor3 = P("#") * C(p_hexdigit*p_hexdigit)^1 / function(r,g,b)
+ r = tonumber(r,16)/255
+ g = g and tonumber(g,16)/255 or r
+ b = b and tonumber(b,16)/255 or g
+ return f_triplet(r,g,b)
+end
+
+local function hexcolor(c)
+ return lpegmatch(p_hexcolor,c)
+end
+
+local function hexcolor3(c)
+ return lpegmatch(p_hexcolor3,c)
+end
+
+-- gains a little:
+
+-- local hexhash = setmetatableindex(function(t,k) local v = lpegmatch(p_hexcolor, k) t[k] = v return v end) -- per file
+-- local hexhash3 = setmetatableindex(function(t,k) local v = lpegmatch(p_hexcolor3,k) t[k] = v return v end) -- per file
+--
+-- local function hexcolor (c) return hexhash [c] end -- directly do hexhash [c]
+-- local function hexcolor3(c) return hexhash3[c] end -- directly do hexhash3[c]
+
+-- Most of the conversion is rather trivial code till I ran into a file with arcs. A bit
+-- of searching lead to the a2c javascript function but it has some puzzling thingies
+-- (like sin and cos definitions that look like leftovers). Anyway, we can if needed
+-- optimize it a bit more. Here does it come from:
+
+-- http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+-- https://github.com/adobe-webplatform/Snap.svg/blob/b242f49e6798ac297a3dad0dfb03c0893e394464/src/path.js
+
+local d120 = (pi * 120) / 180
+local pi2 = 2 * pi
+
+local function a2c(x1, y1, rx, ry, angle, large, sweep, x2, y2, f1, f2, cx, cy)
+
+ local recursive = f1
+
+ if rx == 0 or ry == 0 then
+ return { x1, y1, x2, y2, x2, y2 }
+ end
+
+ if x1 == x2 and y1 == y2 then
+ return { x1, y1, x2, y2, x2, y2 }
+ end
+
+ local rad = pi / 180 * angle
+ local res = nil
+
+ local cosrad = cos(-rad) -- local cosrad = cosd(angle)
+ local sinrad = sin(-rad) -- local sinrad = sind(angle)
+
+ if not recursive then
+
+ x1, y1 = x1 * cosrad - y1 * sinrad, x1 * sinrad + y1 * cosrad
+ x2, y2 = x2 * cosrad - y2 * sinrad, x2 * sinrad + y2 * cosrad
+
+ local x = (x1 - x2) / 2
+ local y = (y1 - y2) / 2
+ local xx = x * x
+ local yy = y * y
+ local h = xx / (rx * rx) + yy / (ry * ry)
+
+ if h > 1 then
+ h = sqrt(h)
+ rx = h * rx
+ ry = h * ry
+ end
+
+ local rx2 = rx * rx
+ local ry2 = ry * ry
+ local ry2xx = ry2 * xx
+ local rx2yy = rx2 * yy
+ local total = rx2yy + ry2xx -- otherwise overflow
+
+ local k = total == 0 and 0 or sqrt(abs((rx2 * ry2 - rx2yy - ry2xx) / total))
+
+ if large == sweep then
+ k = -k
+ end
+
+ cx = k * rx * y / ry + (x1 + x2) / 2
+ cy = k * -ry * x / rx + (y1 + y2) / 2
+
+ f1 = (y1 - cy) / ry -- otherwise crash on a tiny eps
+ f2 = (y2 - cy) / ry -- otherwise crash on a tiny eps
+
+ f1 = asin((f1 < -1.0 and -1.0) or (f1 > 1.0 and 1.0) or f1)
+ f2 = asin((f2 < -1.0 and -1.0) or (f2 > 1.0 and 1.0) or f2)
+
+ if x1 < cx then f1 = pi - f1 end
+ if x2 < cx then f2 = pi - f2 end
+
+ if f1 < 0 then f1 = pi2 + f1 end
+ if f2 < 0 then f2 = pi2 + f2 end
+
+ if sweep ~= 0 and f1 > f2 then
+ f1 = f1 - pi2
+ end
+ if sweep == 0 and f2 > f1 then
+ f2 = f2 - pi2
+ end
+
+ end
+
+ if abs(f2 - f1) > d120 then
+ local f2old = f2
+ local x2old = x2
+ local y2old = y2
+ f2 = f1 + d120 * ((sweep ~= 0 and f2 > f1) and 1 or -1)
+ x2 = cx + rx * cos(f2)
+ y2 = cy + ry * sin(f2)
+ res = a2c(x2, y2, rx, ry, angle, 0, sweep, x2old, y2old, f2, f2old, cx, cy)
+ end
+
+ local c1 = cos(f1)
+ local s1 = sin(f1)
+ local c2 = cos(f2)
+ local s2 = sin(f2)
+
+ local t = tan((f2 - f1) / 4)
+ local hx = 4 * rx * t / 3
+ local hy = 4 * ry * t / 3
+
+ local r = { x1 - hx * s1, y1 + hy * c1, x2 + hx * s2, y2 - hy * c2, x2, y2, unpack(res or { }) }
+
+ if not recursive then -- we can also check for sin/cos being 0/1
+ cosrad = cos(rad)
+ sinrad = sin(rad)
+ -- cosrad = cosd(angle)
+ -- sinrad = sind(angle)
+ for i0=1,#r,2 do
+ local i1 = i0 + 1
+ local x = r[i0]
+ local y = r[i1]
+ r[i0] = x * cosrad - y * sinrad
+ r[i1] = x * sinrad + y * cosrad
+ end
+ end
+
+ return r
+end
+
+-- incredible: we can find .123.456 => 0.123 0.456 ...
+
+local factors = {
+ ["pt"] = 1.25,
+ ["mm"] = 3.543307,
+ ["cm"] = 35.43307,
+ ["px"] = 1,
+ ["pc"] = 15,
+ ["in"] = 90,
+ ["em"] = 12 * 1.25,
+ ["ex"] = 8 * 1.25,
+}
+
+local percentage_r = 1/100
+local percentage_x = percentage_r
+local percentage_y = percentage_r
+
+local p_command_x = C(S("Hh"))
+local p_command_y = C(S("Vv"))
+local p_command_xy = C(S("CcLlMmQqSsTt"))
+local p_command_a = C(S("Aa"))
+local p_command = C(S("Zz"))
+
+local p_separator = S("\t\n\r ,")^1
+local p_number = (S("+-")^0 * (p_digit^0 * P(".") * p_digit^1 + p_digit^1 * P(".") + p_digit^1) )
+
+local function convert (n) n = tonumber(n) return n end
+local function convert_r (n,u) n = tonumber(n) if u == true then return percentage_r * n elseif u then return u * n else return n end end
+local function convert_x (n,u) n = tonumber(n) if u == true then return percentage_x * n elseif u then return u * n else return n end end
+local function convert_y (n,u) n = tonumber(n) if u == true then return percentage_y * n elseif u then return u * n else return n end end
+local function convert_vx(n,u) n = tonumber(n) if u == true then return percentage_x * n elseif u then return u * n else return n end end
+local function convert_vy(n,u) n = - tonumber(n) if u == true then return percentage_y * n elseif u then return u * n else return n end end
+
+local p_unit = ( P("p") * S("txc") + P("e") * S("xm") + S("mc") * P("m") + P("in")) / factors
+local p_percent = P("%") * Cc(true)
+
+local c_number_n = C(p_number)
+local c_number_u = C(p_number) * (p_unit + p_percent)^-1
+
+local p_number_n = c_number_n / convert
+local p_number_x = c_number_u / convert_x
+local p_number_vx = c_number_u / convert_vx
+local p_number_y = c_number_u / convert_y
+local p_number_vy = c_number_u / convert_vy
+local p_number_r = c_number_u / convert_r
+
+-- local p_number = p_number_r -- maybe no percent here
+
+local function asnumber (s) return lpegmatch(p_number, s) end
+local function asnumber_r (s) return lpegmatch(p_number_r, s) end
+local function asnumber_x (s) return lpegmatch(p_number_x, s) end
+local function asnumber_y (s) return lpegmatch(p_number_y, s) end
+local function asnumber_vx(s) return lpegmatch(p_number_vx,s) end
+local function asnumber_vy(s) return lpegmatch(p_number_vy,s) end
+
+local p_numbersep = p_number_n + p_separator
+local p_numbers = p_separator^0 * P("(") * p_numbersep^0 * p_separator^0 * P(")")
+local p_four = p_numbersep^4
+
+-- local p_path = Ct((p_command + (p_number_x * p_separator^0 * p_number_y * p_separator^0) + p_separator)^1)
+
+local p_path = Ct ( (
+ p_command_xy * (p_separator^0 * p_number_vx *
+ p_separator^0 * p_number_vy )^1
+ + p_command_x * (p_separator^0 * p_number_vx )^1
+ + p_command_y * (p_separator^0 * p_number_vy )^1
+ + p_command_a * (p_separator^0 * p_number_vx *
+ p_separator^0 * p_number_vy *
+ p_separator^0 * p_number_r *
+ p_separator^0 * p_number_n * -- flags
+ p_separator^0 * p_number_n * -- flags
+ p_separator^0 * p_number_vx *
+ p_separator^0 * p_number_vy )^1
+ + p_command
+ + p_separator
+)^1 )
+
+local p_rgbacolor = P("rgba(") * (C(p_number) + p_separator)^1 * P(")")
+
+local function rgbacolor(s)
+ local r, g, b, a = lpegmatch(p_rgbacolor,s)
+ if a then
+ return f_rgba(r/255,g/255,b/255,a)
+ end
+end
+
+local function viewbox(v)
+ local x, y, w, h = lpegmatch(p_four,v)
+ if h then
+ return x, y, w, h
+ end
+end
+
+-- actually we can loop faster because we can go to the last one
+
+local function grabpath(str)
+ local p = lpegmatch(p_path,str)
+ local np = #p
+ local t = { } -- no real saving here if we share
+ local n = 0
+ local all = { entries = np, closed = false, curve = false }
+ local a = 0
+ local i = 0
+ local last = "M"
+ local prev = last
+ local kind = "L"
+ local x = 0
+ local y = 0
+ local x1 = 0
+ local y1 = 0
+ local x2 = 0
+ local y2 = 0
+ local rx = 0
+ local ry = 0
+ local ar = 0
+ local al = 0
+ local as = 0
+ local ac = nil
+ while i < np do
+ i = i + 1
+ local pi = p[i]
+ if type(pi) ~= "number" then
+ last = pi
+ i = i + 1
+ pi = p[i]
+ end
+ -- most often
+ ::restart::
+ if last == "c" then
+ x1 = x + pi
+ i = i + 1 ; y1 = y + p[i]
+ i = i + 1 ; x2 = x + p[i]
+ i = i + 1 ; y2 = y + p[i]
+ i = i + 1 ; x = x + p[i]
+ i = i + 1 ; y = y + p[i]
+ goto curveto
+ elseif last == "l" then
+ x = x + pi
+ i = i + 1 ; y = y + p[i]
+ goto lineto
+ elseif last == "h" then
+ x = x + pi
+ goto lineto
+ elseif last == "v" then
+ y = y + pi
+ goto lineto
+ elseif last == "a" then
+ x1 = x
+ y1 = y
+ rx = pi
+ i = i + 1 ; ry = p[i]
+ i = i + 1 ; ar = p[i]
+ i = i + 1 ; al = p[i]
+ i = i + 1 ; as = p[i]
+ i = i + 1 ; x = x + p[i]
+ i = i + 1 ; y = y + p[i]
+ goto arc
+ elseif last == "s" then
+ if prev == "C" then
+ x1 = 2 * x - x2
+ y1 = 2 * y - y2
+ else
+ x1 = x
+ y1 = y
+ end
+ x2 = x + pi
+ i = i + 1 ; y2 = y + p[i]
+ i = i + 1 ; x = x + p[i]
+ i = i + 1 ; y = y + p[i]
+ goto curveto
+ elseif last == "m" then
+ if n > 0 then
+ a = a + 1
+ all[a] = concat(t,"",1,n)
+ n = 0
+ end
+ x = x + pi
+ i = i + 1 ; y = y + p[i]
+ goto moveto
+ elseif last == "z" then
+ goto close
+ -- less frequent
+ elseif last == "C" then
+ x1 = pi
+ i = i + 1 ; y1 = p[i]
+ i = i + 1 ; x2 = p[i]
+ i = i + 1 ; y2 = p[i]
+ i = i + 1 ; x = p[i]
+ i = i + 1 ; y = p[i]
+ goto curveto
+ elseif last == "L" then
+ x = pi
+ i = i + 1 ; y = p[i]
+ goto lineto
+ elseif last == "H" then
+ x = pi
+ goto lineto
+ elseif last == "V" then
+ y = pi
+ goto lineto
+ elseif last == "A" then
+ x1 = x
+ y1 = y
+ rx = pi
+ i = i + 1 ; ry = p[i]
+ i = i + 1 ; ar = p[i]
+ i = i + 1 ; al = p[i]
+ i = i + 1 ; as = p[i]
+ i = i + 1 ; x = p[i]
+ i = i + 1 ; y = p[i]
+ goto arc
+ elseif last == "S" then
+ if prev == "C" then
+ x1 = 2 * x - x2
+ y1 = 2 * y - y2
+ else
+ x1 = x
+ y1 = y
+ end
+ x2 = pi
+ i = i + 1 ; y2 = p[i]
+ i = i + 1 ; x = p[i]
+ i = i + 1 ; y = p[i]
+ goto curveto
+ elseif last == "M" then
+ if n > 0 then
+ a = a + 1 ; all[a] = concat(t,"",1,n) ; n = 0
+ end
+ x = pi ;
+ i = i + 1 ; y = p[i]
+ goto moveto
+ elseif last == "Z" then
+ goto close
+ -- very seldom
+ elseif last == "q" then
+ x1 = x + pi
+ i = i + 1 ; y1 = y + p[i]
+ i = i + 1 ; x2 = x + p[i]
+ i = i + 1 ; y2 = y + p[i]
+ goto quadratic
+ elseif last == "t" then
+ if prev == "C" then
+ x1 = 2 * x - x1
+ y1 = 2 * y - y1
+ else
+ x1 = x
+ y1 = y
+ end
+ x2 = x + pi
+ i = i + 1 ; y2 = y + p[i]
+ goto quadratic
+ elseif last == "Q" then
+ x1 = pi
+ i = i + 1 ; y1 = p[i]
+ i = i + 1 ; x2 = p[i]
+ i = i + 1 ; y2 = p[i]
+ goto quadratic
+ elseif last == "T" then
+ if prev == "C" then
+ x1 = 2 * x - x1
+ y1 = 2 * y - y1
+ else
+ x1 = x
+ y1 = y
+ end
+ x2 = pi
+ i = i + 1 ; y2 = p[i]
+ goto quadratic
+ else
+ goto continue
+ end
+ ::moveto::
+ n = n + 1 ; t[n] = f_moveto(x,y)
+ last = last == "M" and "L" or "l"
+ prev = "M"
+ goto continue
+ ::lineto::
+ n = n + 1 ; t[n] = (n > 0 and f_lineto_n or f_lineto_z)(x,y)
+ prev = "L"
+ goto continue
+ ::curveto::
+ n = n + 1 ; t[n] = (n > 0 and f_curveto_n or f_curveto_z)(x1,y1,x2,y2,x,y)
+ prev = "C"
+ goto continue
+ ::arc::
+ ac = a2c(x1,y1,rx,ry,ar,al,as,x,y)
+ for i=1,#ac,6 do
+ n = n + 1 ; t[n] = (n > 0 and f_curveto_n or f_curveto_z)(
+ ac[i],ac[i+1],ac[i+2],ac[i+3],ac[i+4],ac[i+5]
+ )
+ end
+ prev = "A"
+ goto continue
+ ::quadratic::
+ n = n + 1 ; t[n] = (n > 0 and f_curveto_n or f_curveto_z)(
+ x + 2/3 * (x1-x ), y + 2/3 * (y1-y ),
+ x2 + 2/3 * (x1-x2), y2 + 2/3 * (y1-y2),
+ x2, y2
+ )
+ x = x2
+ y = y2
+ prev = "C"
+ goto continue
+ ::close::
+ n = n + 1 ; t[n] = prev == "C" and "..cycle" or "--cycle"
+ if n > 0 then
+ a = a + 1 ; all[a] = concat(t,"",1,n) ; n = 0
+ end
+ if i == n then
+ break
+ else
+ i = i - 1
+ end
+ kind = prev
+ prev = "Z"
+ ::continue::
+ end
+ if n > 0 then
+ a = a + 1 ; all[a] = concat(t,"",1,n) ; n = 0
+ end
+ if prev == "Z" then
+ all.closed = true
+ end
+ all.curve = (kind == "C" or kind == "A")
+ return all
+end
+
+local transform do
+
+ --todo: better lpeg
+
+ local n = 0
+
+ local function rotate(r,x,y)
+ if x then
+ n = n + 1
+ return r and f_rotatedaround(x,-(y or x),-r)
+ elseif r then
+ n = n + 1
+ return f_rotated(-r)
+ else
+ return ""
+ end
+ end
+
+ local function translate(x,y)
+ if y then
+ n = n + 1
+ return f_shifted(x,-y)
+ elseif x then
+ n = n + 1
+ return f_shifted(x,0)
+ else
+ return ""
+ end
+ end
+
+ local function scale(x,y)
+ if y then
+ n = n + 1
+ return f_xyscaled(x,y)
+ elseif x then
+ n = n + 1
+ return f_scaled(x)
+ else
+ return ""
+ end
+ end
+
+ local function skewx(x)
+ if x then
+ n = n + 1
+ return f_slanted_x(math.sind(-x))
+ else
+ return ""
+ end
+ end
+
+ local function skewy(y)
+ if y then
+ n = n + 1
+ return f_slanted_y(math.sind(-y))
+ else
+ return ""
+ end
+ end
+
+ local function matrix(rx,sx,sy,ry,tx,ty)
+ n = n + 1
+ return f_matrix(rx or 1, sx or 0, sy or 0, ry or 1, tx or 0, - (ty or 0))
+ end
+
+ -- how to deal with units here?
+
+ local p_transform = Cs ( (
+ P("translate") * p_numbers / translate -- maybe xy
+ + P("scale") * p_numbers / scale
+ + P("rotate") * p_numbers / rotate
+ + P("matrix") * p_numbers / matrix
+ + P("skewX") * p_numbers / skewx
+ + P("skewY") * p_numbers / skewy
+ -- + p_separator
+ + P(1)/""
+ )^1)
+
+ transform = function(t,image)
+ n = 0
+ local e = lpegmatch(p_transform,t)
+ local b = rep("( ",n)
+ if image then
+ return f_transform_start(b), f_transform_stop(e)
+ else
+ return b, e
+ end
+ end
+
+end
+
+local dashed do
+
+ -- actually commas are mandate but we're tolerant
+
+ local p_number = p_separator^0/"" * p_number_r
+ local p_on = Cc(" on ") * p_number
+ local p_off = Cc(" off ") * p_number
+ local p_dashed = Cs((p_on * p_off^-1)^1)
+
+ dashed = function(s,o)
+ if not find(s,",") then
+ -- a bit of a hack:
+ s = s .. " " .. s
+ end
+ return (o and f_dashed_y or f_dashed_n)(lpegmatch(p_dashed,s),o)
+ end
+
+end
+
+do
+
+ local defaults = {
+ x = 0, x1 = 0, x2 = 0, cx = 0, rx = 0,
+ y = 0, y1 = 0, y2 = 0, cy = 0, ry = 0,
+ r = 0,
+
+ width = 0,
+ height = 0,
+
+ stroke = "none",
+ fill = "black",
+ opacity = "none",
+
+ ["stroke-width"] = 1,
+ ["stroke-linecap"] = "none",
+ ["stroke-linejoin"] = "none",
+ ["stroke-dasharray"] = "none",
+ ["stroke-dashoffset"] = "none",
+ ["stroke-miterlimit"] = "none",
+ ["stroke-opacity"] = "none",
+
+ ["fill-opacity"] = "none",
+ -- ["fill-rule"] = "nonzero",
+ -- ["clip-rule"] = "nonzero",
+ }
+
+ local handlers = { }
+ local process = false
+ local root = false
+ local result = false
+ local r = false
+ local definitions = false
+ local styles = false
+ local bodyfont = false
+
+ -- todo: check use ... and make definitions[id] self resolving
+
+ local function locate(id)
+ local ref = gsub(id,"^url%(#(.-)%)$","%1")
+ local ref = gsub(ref,"^#","")
+ local res = xmlfirst(root,"**[@id='"..ref.."']")
+ if res then
+ definitions[id] = res
+ return res
+ else
+ -- warning
+ end
+ end
+
+ local function clippath(id,a)
+ local spec = definitions[id] or locate(id)
+ if spec then
+ local kind = spec.tg
+ if kind == "clipPath" then
+ ::again::
+ for c in xmlcollected(spec,"/(path|use|g)") do
+ local tg = c.tg
+ if tg == "use" then
+ local ca = c.at
+ local id = ca["xlink:href"]
+ if id then
+ spec = locate(id)
+ if spec then
+ local sa = spec.at
+ setmetatableindex(sa,ca)
+ if spec.tg == "path" then
+ local d = sa.d
+ if d then
+ local p = grabpath(d)
+ p.evenodd = sa["clip-rule"] == "evenodd" or a["clip-rule"] == "evenodd"
+ return p
+ else
+ goto done
+ end
+ else
+ goto again
+ end
+ end
+ end
+ -- break
+ elseif tg == "path" then
+ local ca = c.at
+ local d = ca.d
+ if d then
+ local p = grabpath(d)
+ p.evenodd = ca["clip-rule"] == "evenodd" or a["clip-rule"] == "evenodd"
+ return p
+ else
+ goto done
+ end
+ else
+ -- inherit?
+ end
+ end
+ ::done::
+ else
+ report("unknown clip %a",id)
+ end
+ end
+ end
+
+ local function gradient(id)
+ local spec = definitions[id]
+ if spec then
+ local kind = spec.tg
+ local shade = nil
+ local n = 1
+ local a = spec.at
+ if kind == "linearGradient" then
+ shade = { s_shade_linear }
+ --
+ local x1 = a.x1
+ local y1 = a.y1
+ local x2 = a.x2
+ local y2 = a.y2
+ if x1 and y1 then
+ n = n + 1 shade[n] = f_shade_one(asnumber_vx(x1),asnumber_vy(y1))
+ end
+ if x2 and y2 then
+ n = n + 1 shade[n] = f_shade_one(asnumber_vx(x2),asnumber_vy(y2))
+ end
+ --
+ elseif kind == "radialGradient" then
+ shade = { s_shade_circular }
+ --
+ local cx = a.cx -- x center
+ local cy = a.cy -- y center
+ local r = a.r -- radius
+ local fx = a.fx -- focal points
+ local fy = a.fy -- focal points
+ --
+ if cx and cy then
+ -- todo
+ end
+ if r then
+ -- todo
+ end
+ if fx and fy then
+ -- todo
+ end
+ else
+ report("unknown gradient %a",id)
+ return
+ end
+ -- local gu = a.gradientUnits
+ -- local gt = a.gradientTransform
+ -- local sm = a.spreadMethod
+ local colora, colorb
+ for c in xmlcollected(spec,"/stop") do
+ local a = c.at
+ local offset = a.offset
+ local colorb = a["stop-color"]
+ local opacity = a["stop-opacity"]
+
+ colorb = colorb and hexcolor3(colorb) or colorb
+ colora = colora or colorb
+
+ -- what if no percentage
+
+ local fraction = offset and asnumber_r(offset)
+
+ if not fraction then
+ -- offset = tonumber(offset)
+ -- for now
+ fraction = xmlcount(spec,"/stop")/100
+ end
+
+ n = n + 1 shade[n] = f_shade_step(fraction,colora,colorb)
+
+ prevcolor = color
+ end
+ return concat(shade)
+ end
+ end
+
+ local function drawproperties(stroke,a)
+ local w = a["stroke-width"]
+ if w then
+ w = f_pen(asnumber_r(w))
+ else
+ w = ""
+ end
+ local d = a["stroke-dasharray"]
+ if d ~= "none" then
+ local o = a["stroke-dashoffset"]
+ if o ~= "none" then
+ o = asnumber_r(o)
+ else
+ o = false
+ end
+ d = dashed(d,o)
+ else
+ d = ""
+ end
+ local c = hexcolor(stroke)
+ if not c then
+ c = rgbacolor(stroke)
+ if not c then
+ c = f_color(stroke)
+ end
+ end
+ local o = a["stroke-opacity"] or a.opacity
+ if o ~= "none" then
+ o = asnumber_r(o)
+ if o and o ~= 1 then
+ o = f_opacity(o)
+ else
+ o = ""
+ end
+ else
+ o = ""
+ end
+ return w, d, c, o
+ end
+
+ local function fillproperties(fill,a)
+ local c = gradient(fill)
+ if not c then
+ c = hexcolor(fill)
+ if not c then
+ c = f_color(fill)
+ end
+ end
+ local o = a["fill-opacity"] or a.opacity
+ if o == "none" then
+ o = asnumber_r(o)
+ if o and o ~= 1 then
+ o = f_opacity(asnumber_r(o))
+ else
+ o = ""
+ end
+ else
+ o = ""
+ end
+ return c, o
+ end
+
+ -- todo: clip = [ auto | rect(llx,lly,urx,ury) ]
+
+ local function offset(a)
+ local x = a.x
+ local y = a.y
+ if x then x = asnumber_vx(x) end
+ if y then y = asnumber_vy(y) end
+ if not x then x = 0 end
+ if not y then y = 0 end
+ if x ~= 0 or y ~= 0 then
+ r = r + 1 ; result[r] = s_offset_start
+ return function()
+ r = r + 1 ; result[r] = f_offset_stop(x,-y)
+ end
+ end
+ end
+
+ local function viewport(x,y,w,h,noclip)
+ r = r + 1 ; result[r] = f_viewport_start
+ return function()
+ r = r + 1 ; result[r] = f_viewport_shift(-x,y)
+ if not noclip then
+ r = r + 1 ; result[r] = f_viewport_clip(w,-h)
+ end
+ r = r + 1 ; result[r] = f_viewport_stop
+ end
+ end
+
+ function handlers.defs(c,top)
+ for c in xmlcollected(c,"/*") do
+ local a = c.at
+ local id = a.id
+ -- setmetatableindex(a,top)
+ if id then
+ definitions["#" .. id ] = c
+ definitions["url(#" .. id .. ")"] = c
+ end
+ end
+ end
+
+ function handlers.use(c,top)
+ local a = setmetatableindex(c.at,top)
+ local id = a["xlink:href"] -- better a rawget
+ if id then
+ local d = definitions[id]
+ if d then
+ local h = handlers[d.tg]
+ if h then
+ h(d,a)
+ end
+ else
+ local res = locate(id)
+ if res then
+ local wrapup = offset(a)
+ process(res,"/*",top)
+ if wrapup then
+ wrapup()
+ end
+ else
+ report("unknown definition %a",id)
+ end
+ end
+ end
+ end
+
+ local linecaps = { butt = "butt", square = "squared", round = "rounded" }
+ local linejoins = { miter = "mitered", bevel = "beveled", round = "rounded" }
+
+ local function stoplineproperties()
+ r = r + 1 result[r] = s_endgroup
+ end
+
+ local function startlineproperties(a)
+ local cap = a["stroke-linecap"]
+ local join = a["stroke-linejoin"]
+ local limit = a["stroke-miterlimit"]
+ cap = cap ~= "none" and linecaps [cap] or false
+ join = join ~= "none" and linejoins[join] or false
+ limit = limit ~= "none" and asnumber_r(limit) or false
+ if cap or join or limit then
+ r = r + 1 result[r] = s_begingroup
+ if cap then
+ r = r + 1 result[r] = f_linecap(cap)
+ end
+ if join then
+ r = r + 1 result[r] = f_linejoin(join)
+ end
+ if limit then
+ r = r + 1 result[r] = f_miterlimit(limit)
+ end
+ return stoplineproperties
+ end
+ end
+
+ local function flush(a,shape,nofill)
+ local fill = not nofill and a.fill
+ local stroke = a.stroke
+ local cpath = a["clip-path"]
+ local trans = a.transform
+ local b, e = "", ""
+ if cpath then
+ cpath = clippath(cpath,a)
+ end
+ if cpath then
+ r = r + 1 result[r] = s_clip_start
+ end
+ if trans then
+ b, e = transform(trans)
+ end
+ if fill and fill ~= "none" then
+ r = r + 1 result[r] = (a["fill-rule"] == "evenodd" and f_eofill or f_fill)(b,shape,e,fillproperties(fill,a))
+ end
+ if stroke and stroke ~= "none" and stroke ~= 0 then
+ local wrapup = startlineproperties(a)
+ r = r + 1 result[r] = f_draw(b,shape,e,drawproperties(stroke,a))
+ if wrapup then
+ wrapup()
+ end
+ end
+ if cpath then
+ r = r + 1 result[r] = (cpath.evenodd and f_eoclip_stop or f_clip_stop)(cpath[1])
+ end
+ end
+
+ -- todo: strokes in:
+
+ local function flushpath(a,shape)
+ local fill = a.fill
+ local stroke = a.stroke
+ local cpath = a["clip-path"]
+ local trans = a.transform
+ local b, e = "", ""
+ if cpath then
+ cpath = definitions[cpath]
+ end
+ if cpath then
+ r = r + 1 result[r] = s_clip_start
+ end
+ -- todo: image (nicer for transform too)
+ if trans then
+ b, e = transform(trans)
+ end
+ if fill and fill ~= "none" then
+ local n = #shape
+ for i=1,n do
+ r = r + 1
+ if i == n then
+ local f = a["fill-rule"] == "evenodd"
+ if shape.closed then
+ f = f and f_eofill or f_fill
+ elseif shape.curve then
+ f = f and f_eofill_cycle_c or f_fill_cycle_c
+ else
+ f = f and f_eofill_cycle_l or f_fill_cycle_l
+ end
+ result[r] = f(b,shape[i],e,fillproperties(fill,a))
+ else
+ result[r] = f_nofill(b,shape[i],e)
+ end
+ end
+ end
+ if stroke and stroke ~= "none" and stroke ~= 0 then
+ local wrapup = startlineproperties(a)
+ local n = #shape
+ for i=1,n do
+ r = r + 1
+ if i == n then
+ result[r] = f_draw(b,shape[i],e,drawproperties(stroke,a))
+ else
+ result[r] = f_nodraw(b,shape[i],e)
+ end
+ end
+ if wrapup then
+ wrapup()
+ end
+ end
+ if cpath then
+ r = r + 1 result[r] = f_clip_stop(cpath[1])
+ end
+ end
+
+ function handlers.line(c,top)
+ local a = setmetatableindex(c.at,top)
+ local x1 = asnumber_vx(a.x1)
+ local y1 = asnumber_vy(a.y1)
+ local x2 = asnumber_vx(a.x2)
+ local y2 = asnumber_vy(a.y2)
+ if trace then
+ report("line: x1 %.3N, y1 %.3N, x2 %.3N, x3 %.3N",x1,y1,x2,y2)
+ end
+ flush(a,f_line(x1,y1,x2,y2),true)
+ end
+
+ function handlers.rect(c,top)
+ local a = setmetatableindex(c.at,top)
+ local w = asnumber_x(a.width)
+ local h = asnumber_y(a.height)
+ local x = asnumber_vx(a.x)
+ local y = asnumber_vy(a.y) - h
+ local rx = a.rx
+ local ry = a.ry
+ if rx == 0 then rx = false end -- maybe no default 0
+ if ry == 0 then ry = false end -- maybe no default 0
+ if rx or ry then
+ rx = asnumber_x(rx or ry)
+ ry = asnumber_y(ry or rx)
+ if trace then
+ report("rect: x %.3N, y %.3N, w %.3N, h %.3N, rx %.3N, ry %.3N",x,y,w,h,rx,ry)
+ end
+ flush(a,f_rounded(w,h,rx,ry,x,y))
+ else
+ if trace then
+ report("rect: x %.3N, y %.3N, w %.3N, h %.3N",x,y,w,h)
+ end
+ flush(a,f_rectangle(w,h,x,y))
+ end
+ end
+
+ function handlers.ellipse(c,top)
+ local a = setmetatableindex(c.at,top)
+ local x = asnumber_vx(a.cx)
+ local y = asnumber_vy(a.cy)
+ local rx = asnumber_x (a.rx)
+ local ry = asnumber_y (a.ry)
+ if trace then
+ report("ellipse: x %.3N, y %.3N, rx %.3N, ry %.3N",x,y,rx,ry)
+ end
+ flush(a,f_ellipse(2*rx,2*ry,x,y))
+ end
+
+ function handlers.circle(c,top)
+ local a = setmetatableindex(c.at,top)
+ local x = asnumber_vx(a.cx)
+ local y = asnumber_vy(a.cy)
+ local r = asnumber_r (a.r)
+ if trace then
+ report("circle: x %.3N, y %.3N, r %.3N",x,y,r)
+ end
+ flush(a,f_circle(2*r,x,y))
+ end
+
+ function handlers.path(c,top)
+ local a = setmetatableindex(c.at,top)
+ local d = a.d
+ if d then
+ local p = grabpath(d)
+ if trace then
+ report("path: %i entries, %sclosed",p.entries,p.closed and "" or "not ")
+ end
+ flushpath(a,p)
+ end
+ end
+
+ do
+
+ local p_pair = p_separator^0 * p_number_vx * p_separator^0 * p_number_vy
+ local p_pair_z = p_pair / f_lineto_z
+ local p_pair_n = p_pair / f_lineto_n
+ local p_open = Cc("(")
+ local p_close = Carg(1) * P(true) / function(s) return s end
+ local p_polyline = Cs(p_open * p_pair_z * p_pair_n^0 * p_close)
+
+ function handlers.polyline(c,top)
+ local a = setmetatableindex(c.at,top)
+ local p = a.points
+ if p then
+ flush(a,lpegmatch(p_polyline,p,1,")"),true)
+ end
+ end
+
+ function handlers.polygon(c,top)
+ local a = setmetatableindex(c.at,top)
+ local p = a.points
+ if p then
+ flush(a,lpegmatch(p_polyline,p,1,"--cycle)"))
+ end
+ end
+
+ end
+
+ function handlers.g(c,top)
+ local a = c.at
+ setmetatableindex(a,top)
+ process(c,"/*",a)
+ end
+
+ function handlers.symbol(c,top)
+ report("todo: %s","symbol")
+ end
+
+ -- We only need to filter classes .. I will complete this when I really need
+ -- it. Not hard to do but kind of boring.
+
+ local p_class = P(".") * C((R("az","AZ","09","__","--")^1))
+ local p_spec = P("{") * C((1-P("}"))^1) * P("}")
+
+ local p_grab = ((Carg(1) * p_class * p_space^0 * p_spec / function(t,k,v) t[k] = v end) + p_space^1 + P(1))^1
+
+ function handlers.style(c,top)
+ local s = xmltext(c)
+ lpegmatch(p_grab,s,1,styles)
+ for k, v in next, styles do
+ -- todo: lpeg
+ local t = { }
+ for k, v in gmatch(v,"%s*([^:]+):%s*([^;]+);") do
+ if k == "font" then
+ v = xml.css.fontspecification(v)
+ end
+ t[k] = v
+ end
+ styles[k] = t
+ end
+ bodyfont = tex.getdimen("bodyfontsize") / 65536
+ end
+
+ -- this will never really work out
+
+ function handlers.text(c,top)
+ local a = c.at
+ local x = asnumber_vx(a.x)
+ local y = asnumber_vy(a.y)
+ local text = xmltext(c)
+ local size = false
+ local fill = a.fill
+ -- escape text or keep it at the tex end
+ local class = a.class
+ if class then
+ local style = styles[class]
+ if style and next(style) then
+ local font = style.font
+ local s_family = font and font.family
+ local s_style = font and font.style
+ local s_weight = font and font.weight
+ local s_size = font and font.size
+ if s_size then
+ local f = factors[s_size[2]]
+ if f then
+ size = f * s_size[1]
+ end
+ end
+ -- todo: family: serif | sans-serif | monospace : use mapping
+ if s_family then
+ if s_family == "serif" then
+ text = "\\rm " .. text
+ elseif s_family == "sans-serif" then
+ text = "\\ss " .. text
+ else
+ text = "\\tt " .. text
+ end
+ end
+ if s_style or s_weight then
+ text = formatters['\\style[%s%s]{%s}'](s_weight or "",s_style or "",text)
+ end
+ fill = style.fill or fill
+ end
+ end
+ if size then
+ -- bodyfontsize
+ size = size/bodyfont
+ text = f_text_scaled(text,size,x,y)
+ else
+ text = f_text_normal(text,x,y)
+ end
+ if fill and fill ~= "none" then
+ text = f_draw("",text,"",fillproperties(fill,a))
+ else
+ text = f_draw("",text,"","","","","")
+ end
+ if trace then
+ report("text: x %.3N, y %.3N, text %s",x,y,text)
+ end
+ r = r + 1 ; result[r] = text
+ end
+
+ function handlers.svg(c,top,x,y,w,h,noclip)
+ local a = setmetatableindex(c.at,top)
+ local v = a.viewBox
+ local t = a.transform
+ local wrapupoffset
+ local wrapupviewport
+ -- local ex, em
+ local xpct, ypct, rpct
+ if trace then
+ report("view: %s, xpct %.3N, ypct %.3N","before",percentage_x,percentage_y)
+ end
+ if v then
+ x, y, w, h = viewbox(v)
+ if trace then
+ report("viewbox: x %.3N, y %.3N, width %.3N, height %.3N",x,y,w,h)
+ end
+ end
+ if h then
+ --
+ -- em = factors["em"]
+ -- ex = factors["ex"]
+ -- factors["em"] = em
+ -- factors["ex"] = ex
+ --
+ xpct = percentage_x
+ ypct = percentage_y
+ rpct = percentage_r
+ percentage_x = w / 100
+ percentage_y = h / 100
+ percentage_r = (sqrt(w^2 + h^2) / sqrt(2)) / 100
+ if trace then
+ report("view: %s, xpct %.3N, ypct %.3N","inside",percentage_x,percentage_y)
+ end
+ wrapupviewport = viewport(x,y,w,h,noclip)
+ end
+ -- todo: combine transform and offset here
+ if t then
+ btransform, etransform = transform(t,true)
+ end
+ if btransform then
+ r = r + 1 ; result[r] = btransform
+ end
+ wrapupoffset = offset(a)
+ process(c,"/*",top or defaults)
+ if wrapupoffset then
+ wrapupoffset()
+ end
+ if etransform then
+ r = r + 1 ; result[r] = etransform
+ end
+ if h then
+ --
+ -- factors["em"] = em
+ -- factors["ex"] = ex
+ --
+ percentage_x = xpct
+ percentage_y = ypct
+ percentage_r = rpct
+ if wrapupviewport then
+ wrapupviewport()
+ end
+ end
+ if trace then
+ report("view: %s, xpct %.3N, ypct %.3N","after",percentage_x,percentage_y)
+ end
+ end
+
+ process = function(x,p,top)
+ for c in xmlcollected(x,p) do
+ local h = handlers[c.tg]
+ if h then
+ h(c,top)
+ end
+ end
+ end
+
+ function metapost.svgtomp(specification,pattern)
+ local mps = ""
+ local svg = specification.data
+ if type(svg) == "string" then
+ svg = xmlconvert(svg)
+ end
+ if svg then
+ local c = xmlfirst(svg,pattern or "/svg")
+ if c then
+ root, result, r, definitions, styles, bodyfont = svg, { }, 0, { }, { }, 12
+ -- print("default",x,y,w,h)
+ handlers.svg (
+ c,
+ nil,
+ specification.x,
+ specification.y,
+ specification.w,
+ specification.h,
+ specification.noclip
+ )
+ mps = concat(result," ")
+ root, result, r, definitions, styles, bodyfont = false, false, false, false, false, false
+ else
+ report("missing svg root element")
+ end
+ else
+ report("bad svg blob")
+ end
+ return mps
+ end
+
+end
+
+function metapost.showsvgpage(data)
+ local dd = data.data
+ if type(dd) == "table" then
+ local comment = dd.comment
+ local offset = dd.pageoffset
+ for i=1,#dd do
+ local d = setmetatableindex( {
+ data = dd[i],
+ comment = comment and i or false,
+ pageoffset = offset or nil,
+ }, data)
+ metapost.showsvgpage(d)
+ end
+ else
+ context.startMPpage { instance = "doublefun", offset = data.pageoffset or nil }
+ context(metapost.svgtomp(data))
+ local comment = data.comment
+ if comment then
+ context("draw boundingbox currentpicture withcolor .6red ;")
+ context('draw textext.bot("\\strut\\tttf %s") ysized (10pt) shifted center bottomboundary currentpicture ;',comment)
+ end
+ context.stopMPpage()
+ end
+end
+
+function metapost.svgtopdf(data,...)
+ local mps = metapost.svgtomp(data,...)
+ if mps then
+ local pdf = metapost.simple("metafun",mps,true) -- todo: special instance, only basics needed
+ if pdf then
+ return pdf
+ else
+ -- message
+ end
+ else
+ -- message
+ end
+end
+
+do
+
+ local runner = sandbox.registerrunner {
+ name = "otfsvg2pdf",
+ program = "context",
+ template = "--batchmode --purgeall --runs=2 %filename%",
+ reporter = report_svg,
+ }
+
+ -- By using an independent pdf file instead of pdf streams we can use resources and still
+ -- cache. This is the old method updated. Maybe a future version will just do this runtime
+ -- but for now this is the most efficient method.
+
+ function metapost.svgshapestopdf(svgshapes,pdftarget,report_svg)
+ local texname = "temp-otf-svg-to-pdf.tex"
+ local pdfname = "temp-otf-svg-to-pdf.pdf"
+ local tucname = "temp-otf-svg-to-pdf.tuc"
+ local nofshapes = #svgshapes
+ local pdfpages = { filename = pdftarget }
+ local pdfpage = 0
+ local t = { }
+ local n = 0
+ --
+ os.remove(texname)
+ os.remove(pdfname)
+ os.remove(tucname)
+ --
+ if report_svg then
+ report_svg("processing %i svg containers",nofshapes)
+ statistics.starttiming(pdfpages)
+ end
+ --
+ -- can be option:
+ --
+ n = n + 1 ; t[n] = "\\enabledirectives[pdf.stripzeros,metapost.stripzeros]"
+ --
+ -- can be option:
+ --
+ -- n = n + 1 ; t[n] = "\\nopdfcompression"
+ --
+ n = n + 1 ; t[n] = "\\starttext"
+ n = n + 1 ; t[n] = "\\setupMPpage[alternative=offset,instance=doublefun]"
+ --
+ for i=1,nofshapes do
+ local entry = svgshapes[i]
+ local data = entry.data
+ local specification = {
+ data = xmlconvert(data),
+ x = 0,
+ y = 1000,
+ width = 1000,
+ height = 1000,
+ noclip = true,
+ }
+ for index=entry.first,entry.last do
+ if not pdfpages[index] then
+ pdfpage = pdfpage + 1
+ pdfpages[index] = pdfpage
+ local pattern = "/svg[@id='glyph" .. index .. "']"
+ n = n + 1 ; t[n] = "\\startMPpage"
+ n = n + 1 ; t[n] = metapost.svgtomp(specification,pattern) or ""
+ n = n + 1 ; t[n] = "\\stopMPpage"
+ end
+ end
+ end
+ n = n + 1 ; t[n] = "\\stoptext"
+ io.savedata(texname,concat(t,"\n"))
+ runner { filename = texname }
+ os.remove(pdftarget)
+ file.copy(pdfname,pdftarget)
+ if report_svg then
+ statistics.stoptiming(pdfpages)
+ report_svg("svg conversion time %s",statistics.elapsedseconds(pdfpages))
+ end
+ os.remove(texname)
+ os.remove(pdfname)
+ os.remove(tucname)
+ return pdfpages
+ end
+
+ function metapost.svgshapestomp(svgshapes,report_svg)
+ local nofshapes = #svgshapes
+ local mpshapes = { }
+ if report_svg then
+ report_svg("processing %i svg containers",nofshapes)
+ statistics.starttiming(mpshapes)
+ end
+ for i=1,nofshapes do
+ local entry = svgshapes[i]
+ local data = entry.data
+ local specification = {
+ data = xmlconvert(data),
+ x = 0,
+ y = 1000,
+ width = 1000,
+ height = 1000,
+ noclip = true,
+ }
+ for index=entry.first,entry.last do
+ if not mpshapes[index] then
+ local pattern = "/svg[@id='glyph" .. index .. "']"
+ mpshapes[index] = metapost.svgtomp(specification,pattern) or ""
+ end
+ end
+ end
+ if report_svg then
+ statistics.stoptiming(mpshapes)
+ report_svg("svg conversion time %s",statistics.elapsedseconds(mpshapes))
+ end
+ return mpshapes
+ end
+
+end
diff --git a/tex/context/base/mkiv/mult-def.lua b/tex/context/base/mkiv/mult-def.lua
index cb3b4f4e5..3c6ccceaf 100644
--- a/tex/context/base/mkiv/mult-def.lua
+++ b/tex/context/base/mkiv/mult-def.lua
@@ -14336,6 +14336,10 @@ return {
["pe"]="درون‌متن",
["ro"]="intext",
},
+ ["headintext"]={
+ ["en"]="headintext",
+ ["nl"]="kopintekst",
+ },
["intro"]={
["cs"]="uvod",
["de"]="intro",
diff --git a/tex/context/base/mkiv/mult-fun.lua b/tex/context/base/mkiv/mult-fun.lua
index d18d1bc0e..99700f474 100644
--- a/tex/context/base/mkiv/mult-fun.lua
+++ b/tex/context/base/mkiv/mult-fun.lua
@@ -30,6 +30,7 @@ return {
"getparametercount",
"getmaxparametercount",
"getparameterpath",
+ "getparameterpen",
"getparametertext",
-- "getparameteroption",
"applyparameters",
diff --git a/tex/context/base/mkiv/mult-mps.lua b/tex/context/base/mkiv/mult-mps.lua
index 35035308e..625e8ca65 100644
--- a/tex/context/base/mkiv/mult-mps.lua
+++ b/tex/context/base/mkiv/mult-mps.lua
@@ -117,7 +117,7 @@ return {
--
"mm", "pt", "dd", "bp", "cm", "pc", "cc", "in",
--
- "triplet", "quadruplet", "totransform",
+ "triplet", "quadruplet", "totransform", "bymatrix",
--
},
internals = { -- we need to remove duplicates above
diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf
index 75ea1077e..410f87641 100644
--- a/tex/context/base/mkiv/status-files.pdf
+++ b/tex/context/base/mkiv/status-files.pdf
Binary files differ
diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf
index 726657bc3..8008a61ee 100644
--- a/tex/context/base/mkiv/status-lua.pdf
+++ b/tex/context/base/mkiv/status-lua.pdf
Binary files differ
diff --git a/tex/context/base/mkiv/strc-itm.mklx b/tex/context/base/mkiv/strc-itm.mklx
index eeeb5ce00..e5458b5dd 100644
--- a/tex/context/base/mkiv/strc-itm.mklx
+++ b/tex/context/base/mkiv/strc-itm.mklx
@@ -198,6 +198,7 @@
\newconditional\c_strc_itemgroups_txt
\newconditional\c_strc_itemgroups_extra
\newconditional\c_strc_itemgroups_repeat
+\newconditional\c_strc_itemgroups_inline_head
% 0 = before/after
% 1 = between unless before
@@ -440,6 +441,7 @@
\setitemgroupparameter\c!width{0em}%
\fi} % signal
\setvalue{\??itemgroupkeyword\v!intext }{\settrue\c_strc_itemgroups_inline}
+\setvalue{\??itemgroupkeyword\v!headintext }{\settrue\c_strc_itemgroups_inline_head}
\setvalue{\??itemgroupkeyword\v!loose }{\setfalse\c_strc_itemgroups_optimize}
\setvalue{\??itemgroupkeyword\v!fit }{\settrue\c_strc_itemgroups_fitting}
\setvalue{\??itemgroupkeyword\v!nofit }{\setfalse\c_strc_itemgroups_fitting}
@@ -1211,19 +1213,25 @@
\unexpanded\def\strc_itemgroups_stop_head_indeed
{\removeunwantedspaces
\dostoptagged
- \ifconditional\c_strc_itemgroups_text
- \space
- \ignorespaces
+ \ifconditional\c_strc_itemgroups_inline_head
+ \space
+ \ignorespaces
\else
- \par
- \fi
- \strc_itemgroups_insert_breakno
- \ifconditional\c_strc_itemgroups_pack\else\strc_itemgroups_after_head_command\fi
- \strc_itemgroups_insert_breakno}
+ \ifconditional\c_strc_itemgroups_text
+ \space
+ \ignorespaces
+ \else
+ \par
+ \fi
+ \strc_itemgroups_insert_breakno
+ \ifconditional\c_strc_itemgroups_pack\else\strc_itemgroups_after_head_command\fi
+ \strc_itemgroups_insert_breakno
+ \fi}
\unexpanded\def\strc_itemgroups_head_body_indeed
{\dostarttagged\t!itembody\empty
- \noindentation}
+ \noindentation
+ \ignorespaces}
% Simple commands.
diff --git a/tex/context/base/mkiv/strc-itm.mkvi b/tex/context/base/mkiv/strc-itm.mkvi
index bc140d65b..f09b24cdf 100644
--- a/tex/context/base/mkiv/strc-itm.mkvi
+++ b/tex/context/base/mkiv/strc-itm.mkvi
@@ -198,6 +198,7 @@
\newconditional\c_strc_itemgroups_txt
\newconditional\c_strc_itemgroups_extra
\newconditional\c_strc_itemgroups_repeat
+\newconditional\c_strc_itemgroups_inline_head
% 0 = before/after
% 1 = between unless before
@@ -442,6 +443,7 @@
\setitemgroupparameter\c!width{0em}%
\fi} % signal
\setvalue{\??itemgroupkeyword\v!intext }{\settrue\c_strc_itemgroups_inline}
+\setvalue{\??itemgroupkeyword\v!headintext }{\settrue\c_strc_itemgroups_inline_head}
\setvalue{\??itemgroupkeyword\v!loose }{\setfalse\c_strc_itemgroups_optimize}
\setvalue{\??itemgroupkeyword\v!fit }{\settrue\c_strc_itemgroups_fitting}
\setvalue{\??itemgroupkeyword\v!nofit }{\setfalse\c_strc_itemgroups_fitting}
@@ -1232,19 +1234,25 @@
\unexpanded\def\strc_itemgroups_stop_head_indeed
{\removeunwantedspaces
\dostoptagged
- \ifconditional\c_strc_itemgroups_text
- \space
- \ignorespaces
+ \ifconditional\c_strc_itemgroups_inline_head
+ \space
+ \ignorespaces
\else
- \par
- \fi
- \strc_itemgroups_insert_breakno
- \ifconditional\c_strc_itemgroups_pack\else\strc_itemgroups_after_head_command\fi
- \strc_itemgroups_insert_breakno}
+ \ifconditional\c_strc_itemgroups_text
+ \space
+ \ignorespaces
+ \else
+ \par
+ \fi
+ \strc_itemgroups_insert_breakno
+ \ifconditional\c_strc_itemgroups_pack\else\strc_itemgroups_after_head_command\fi
+ \strc_itemgroups_insert_breakno
+ \fi}
\unexpanded\def\strc_itemgroups_head_body_indeed
{\dostarttagged\t!itembody\empty
- \noindentation}
+ \noindentation
+ \ignorespaces}
% Simple commands.
diff --git a/tex/context/base/mkiv/strc-ref.lua b/tex/context/base/mkiv/strc-ref.lua
index ec5ed5e03..a4f31ec0c 100644
--- a/tex/context/base/mkiv/strc-ref.lua
+++ b/tex/context/base/mkiv/strc-ref.lua
@@ -2613,14 +2613,14 @@ implement {
-- actions = { referencerealpage, context },
-- -- arguments = "string" -- hm, weird
-- }
---
--- implement {
--- name = "referencerealpage",
--- actions = function()
--- local actions = references.currentset
--- context(not actions and 0 or actions.realpage or setreferencerealpage(actions))
--- end
--- }
+
+implement {
+ name = "referencerealpage",
+ actions = function()
+ local actions = references.currentset
+ context(not actions and 0 or actions.realpage or setreferencerealpage(actions))
+ end
+}
local function referencepos(key)
local actions = references.currentset
diff --git a/tex/context/base/mkiv/syst-aux.mkiv b/tex/context/base/mkiv/syst-aux.mkiv
index 1cade869c..255f0746c 100644
--- a/tex/context/base/mkiv/syst-aux.mkiv
+++ b/tex/context/base/mkiv/syst-aux.mkiv
@@ -5250,7 +5250,7 @@
\unexpanded\def\pickupgroupedcommand#1#2#3%
{\def\m_syst_helpers_handle_group_b{#1}%
\def\m_syst_helpers_handle_group_a{#2}%
- \def\m_syst_helpers_handle_group_p{#2}%
+ \def\m_syst_helpers_handle_group_p{#3}%
\doifelsenextbgroupcs\syst_helpers_handle_group_pickup\syst_helpers_handle_group_nop}
\unexpanded\def\triggergroupedcommand#1%
diff --git a/tex/context/base/mkiv/syst-aux.mkxl b/tex/context/base/mkiv/syst-aux.mkxl
index b2fc08487..c14e03008 100644
--- a/tex/context/base/mkiv/syst-aux.mkxl
+++ b/tex/context/base/mkiv/syst-aux.mkxl
@@ -4007,7 +4007,7 @@
\unexpanded\def\pickupgroupedcommand#1#2#3%
{\def\m_syst_helpers_handle_group_b{#1}%
\def\m_syst_helpers_handle_group_a{#2}%
- \def\m_syst_helpers_handle_group_p{#2}%
+ \def\m_syst_helpers_handle_group_p{#3}%
\futureexpandis\bgroup\syst_helpers_handle_group_pickup\syst_helpers_handle_group_nop}
\unexpanded\def\triggergroupedcommand#1%
diff --git a/tex/context/base/mkiv/trac-deb.lua b/tex/context/base/mkiv/trac-deb.lua
index 5101119d7..72294e587 100644
--- a/tex/context/base/mkiv/trac-deb.lua
+++ b/tex/context/base/mkiv/trac-deb.lua
@@ -242,7 +242,10 @@ function tracers.printerror(specification)
local offset = specification.offset
local report = errorreporter(luaerrorline)
if not filename then
- report("error not related to input file: %s ...",lasttexerror)
+ report("error not related to input file:")
+ report(" tex: %s",lasttexerror or "-")
+ report(" lua: %s",lastluaerror or "-")
+ report(" mps: %s",lastmpserror or "-")
elseif type(filename) == "number" then
report("error on line %s of filehandle %s: %s ...",linenumber,lasttexerror)
else
diff --git a/tex/context/base/mkiv/util-lua.lua b/tex/context/base/mkiv/util-lua.lua
index f7e6e4f31..fb4088e7b 100644
--- a/tex/context/base/mkiv/util-lua.lua
+++ b/tex/context/base/mkiv/util-lua.lua
@@ -28,6 +28,13 @@ luautilities.nofstrippedbytes = 0
local strippedchunks = { } -- allocate()
luautilities.strippedchunks = strippedchunks
+if not LUATEXENGINE then
+ --- probably mtxrun ...
+ LUATEXENGINE = status.luatex_engine and string.lower(status.luatex_engine)
+ JITSUPPORTED = LUATEXENGINE == "luajittex" or jit
+ CONTEXTLMTXMODE = CONTEXTLMTXMODE or (LUATEXENGINE == "luametatex" and 1) or 0
+end
+
luautilities.suffixes = {
tma = "tma",
tmc = (CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 and "tmd") or (jit and "tmb") or "tmc",
@@ -37,7 +44,8 @@ luautilities.suffixes = {
luv = "luv",
luj = "luj",
tua = "tua",
- tuc = "tuc",
+ -- tuc = "tuc",
+ tuc = (CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 and "tud") or (jit and "tub") or "tuc",
}
-- environment.loadpreprocessedfile can be set to a preprocessor
diff --git a/tex/context/base/mkiv/util-sac.lua b/tex/context/base/mkiv/util-sac.lua
index 976989a77..19dc0eb0f 100644
--- a/tex/context/base/mkiv/util-sac.lua
+++ b/tex/context/base/mkiv/util-sac.lua
@@ -43,6 +43,8 @@ function streams.size(f)
return f and f[3] or 0
end
+streams.getsize = streams.size
+
function streams.setposition(f,i)
if f[4] then
-- zerobased
@@ -485,3 +487,58 @@ else
end
end
+
+-- For practical reasons we put this here. It's less efficient but ok when we don't
+-- have much access.
+
+do
+
+ local files = utilities.files
+
+ if files then
+
+ local openfile = files.open
+ local openstream = streams.open
+ local openstring = streams.openstring
+
+ local setmetatable = setmetatable
+
+ function io.newreader(str,method)
+ local f, m
+ if method == "string" then
+ f = openstring(str)
+ m = streams
+ elseif method == "stream" then
+ f = openstream(str)
+ m = streams
+ else
+ f = openfile(str,"rb")
+ m = files
+ end
+ if f then
+ local t = { }
+ setmetatable(t, {
+ __index = function(t,k)
+ local r = m[k]
+ if k == "close" then
+ if f then
+ m.close(f)
+ f = nil
+ end
+ return function() end
+ elseif r then
+ local v = function(_,a,b) return r(f,a,b) end
+ t[k] = v
+ return v
+ else
+ print("unknown key",k)
+ end
+ end
+ } )
+ return t
+ end
+ end
+
+ end
+
+end
diff --git a/tex/context/interface/mkii/keys-en.xml b/tex/context/interface/mkii/keys-en.xml
index 3b2b69e0b..a621f03ab 100644
--- a/tex/context/interface/mkii/keys-en.xml
+++ b/tex/context/interface/mkii/keys-en.xml
@@ -237,6 +237,7 @@
<cd:variable name='hanging' value='hanging'/>
<cd:variable name='head' value='head'/>
<cd:variable name='header' value='header'/>
+ <cd:variable name='headintext' value='headintext'/>
<cd:variable name='height' value='height'/>
<cd:variable name='helptext' value='helptext'/>
<cd:variable name='hencefore' value='hencefore'/>
diff --git a/tex/context/interface/mkii/keys-it.xml b/tex/context/interface/mkii/keys-it.xml
index ac2816578..f6982abca 100644
--- a/tex/context/interface/mkii/keys-it.xml
+++ b/tex/context/interface/mkii/keys-it.xml
@@ -237,6 +237,7 @@
<cd:variable name='hanging' value='sospeso'/>
<cd:variable name='head' value='testa'/>
<cd:variable name='header' value='intestazione'/>
+ <cd:variable name='headintext' value='headintext'/>
<cd:variable name='height' value='altezza'/>
<cd:variable name='helptext' value='testoaiuto'/>
<cd:variable name='hencefore' value='precedente'/>
@@ -873,6 +874,7 @@
<cd:constant name='hfil' value='hfil'/>
<cd:constant name='hidenumber' value='hidenumber'/>
<cd:constant name='hoffset' value='hoffset'/>
+ <cd:constant name='horizontal' value='orizzontale'/>
<cd:constant name='horoffset' value='horoffset'/>
<cd:constant name='hyphen' value='hyphen'/>
<cd:constant name='hyphens' value='hyphens'/>
@@ -973,9 +975,12 @@
<cd:constant name='menu' value='menu'/>
<cd:constant name='method' value='metodo'/>
<cd:constant name='middle' value='centro'/>
+ <cd:constant name='middlecolor' value='middlecolor'/>
<cd:constant name='middlecommand' value='middlecommand'/>
<cd:constant name='middlespeech' value='middlespeech'/>
+ <cd:constant name='middlestyle' value='middlestyle'/>
<cd:constant name='middletext' value='testocentro'/>
+ <cd:constant name='middlewidth' value='middlewidth'/>
<cd:constant name='midsentence' value='midsentence'/>
<cd:constant name='min' value='min'/>
<cd:constant name='mindepth' value='mindepth'/>
@@ -1293,6 +1298,7 @@
<cd:constant name='vcompact' value='vcompact'/>
<cd:constant name='vector' value='vector'/>
<cd:constant name='veroffset' value='veroffset'/>
+ <cd:constant name='vertical' value='verticale'/>
<cd:constant name='vfactor' value='vfactor'/>
<cd:constant name='vfil' value='vfil'/>
<cd:constant name='viewerprefix' value='viewerprefix'/>
@@ -1303,6 +1309,7 @@
<cd:constant name='white' value='bianco'/>
<cd:constant name='width' value='ampiezza'/>
<cd:constant name='words' value='words'/>
+ <cd:constant name='xanchor' value='xanchor'/>
<cd:constant name='xfactor' value='xfactor'/>
<cd:constant name='xhtml' value='xhtml'/>
<cd:constant name='xmax' value='xmax'/>
@@ -1310,6 +1317,7 @@
<cd:constant name='xoffset' value='xoffset'/>
<cd:constant name='xscale' value='xscale'/>
<cd:constant name='xstep' value='xstep'/>
+ <cd:constant name='yanchor' value='yanchor'/>
<cd:constant name='yfactor' value='yfactor'/>
<cd:constant name='ymax' value='ymax'/>
<cd:constant name='yoffset' value='yoffset'/>
diff --git a/tex/context/interface/mkii/keys-ro.xml b/tex/context/interface/mkii/keys-ro.xml
index 12c7a8606..ca739d2b2 100644
--- a/tex/context/interface/mkii/keys-ro.xml
+++ b/tex/context/interface/mkii/keys-ro.xml
@@ -237,6 +237,7 @@
<cd:variable name='hanging' value='suspendat'/>
<cd:variable name='head' value='antet'/>
<cd:variable name='header' value='antet'/>
+ <cd:variable name='headintext' value='headintext'/>
<cd:variable name='height' value='inaltime'/>
<cd:variable name='helptext' value='textajutator'/>
<cd:variable name='hencefore' value='precedent'/>
diff --git a/tex/context/interface/mkiv/context-en.xml b/tex/context/interface/mkiv/context-en.xml
index 17bc09c8e..3953a8a04 100644
--- a/tex/context/interface/mkiv/context-en.xml
+++ b/tex/context/interface/mkiv/context-en.xml
@@ -17651,6 +17651,7 @@
<cd:constant type="inmargin"/>
<cd:constant type="atmargin"/>
<cd:constant type="intext"/>
+ <cd:constant type="headintext"/>
<cd:constant type="loose"/>
<cd:constant type="fit"/>
<cd:constant type="nofit"/>
@@ -18310,6 +18311,7 @@
<cd:constant type="inmargin"/>
<cd:constant type="atmargin"/>
<cd:constant type="intext"/>
+ <cd:constant type="headintext"/>
<cd:constant type="loose"/>
<cd:constant type="fit"/>
<cd:constant type="nofit"/>
diff --git a/tex/context/interface/mkiv/i-context.pdf b/tex/context/interface/mkiv/i-context.pdf
index a6e83085f..5c0fba19a 100644
--- a/tex/context/interface/mkiv/i-context.pdf
+++ b/tex/context/interface/mkiv/i-context.pdf
Binary files differ
diff --git a/tex/context/interface/mkiv/i-itemgroup.xml b/tex/context/interface/mkiv/i-itemgroup.xml
index 416cf1ccd..810024351 100644
--- a/tex/context/interface/mkiv/i-itemgroup.xml
+++ b/tex/context/interface/mkiv/i-itemgroup.xml
@@ -20,6 +20,7 @@
<cd:constant type="inmargin"/>
<cd:constant type="atmargin"/>
<cd:constant type="intext"/>
+ <cd:constant type="headintext"/>
<cd:constant type="loose"/>
<cd:constant type="fit"/>
<cd:constant type="nofit"/>
diff --git a/tex/context/interface/mkiv/i-readme.pdf b/tex/context/interface/mkiv/i-readme.pdf
index 4d002f1b0..db5adf50d 100644
--- a/tex/context/interface/mkiv/i-readme.pdf
+++ b/tex/context/interface/mkiv/i-readme.pdf
Binary files differ
diff --git a/tex/context/modules/mkiv/m-scite.mkiv b/tex/context/modules/mkiv/m-scite.mkiv
index 5d192a5f6..315cb3bed 100644
--- a/tex/context/modules/mkiv/m-scite.mkiv
+++ b/tex/context/modules/mkiv/m-scite.mkiv
@@ -334,9 +334,12 @@ moduledata.scite = scite
\newdimen\scitespaceskip
\unexpanded\def\buff_scite_slxb#1%
- {\hangindent\numexpr#1+2\relax\scitespaceskip
+ {% we can have a side float
+ \advance\hangindent\numexpr#1+2\relax\scitespaceskip
\begstrut\hskip#1\scitespaceskip
- \hangafter 1\relax}
+ \ifcase\hangafter
+ \hangafter\plusone\relax
+ \fi}
\unexpanded\def\buff_scite_slxe
{\endstrut\par}
diff --git a/tex/context/modules/mkiv/s-fonts-variable.mkiv b/tex/context/modules/mkiv/s-fonts-variable.mkiv
index 2cd612c75..5336f6024 100644
--- a/tex/context/modules/mkiv/s-fonts-variable.mkiv
+++ b/tex/context/modules/mkiv/s-fonts-variable.mkiv
@@ -49,6 +49,8 @@
% \enabletrackers[modules.fonts.variables.glyphs]
% \enabletrackers[modules.fonts.variables.kerns]
+\nopdfcompression
+
\starttext
\startbuffer[zycon]
@@ -81,9 +83,9 @@
% \showfontvariations
% [font=file:DecoVar-VF.ttf]
- % \showfontvariations
- % [font=file:VotoSerifGX.ttf,
- % max=15]
+% \showfontvariations
+% [font=file:VotoSerifGX.ttf,
+% max=15]
% \showfontvariations
% [font=file:Selawik-Variable.ttf]
@@ -91,23 +93,23 @@
% \showfontvariations
% [font=file:LibreFranklinGX-Romans.ttf]
- % \showfontvariations
- % [font=file:Zycon.ttf,
- % sample={\getbuffer[zycon]}]
+% \showfontvariations
+% [font=file:Zycon.ttf,
+% sample={\getbuffer[zycon]}]
- % \showfontvariations
- % [font=file:kairossansvariable.ttf]
+% \showfontvariations
+% [font=file:kairossansvariable.ttf]
- % \showfontvariations
- % [font=file:sourcecode-regular.otf]
+% \showfontvariations
+% [font=file:sourcecode-regular.otf]
- % \showfontvariations
- % [font=file:AmstelvarAlpha-VF.ttf]
+% \showfontvariations
+% [font=file:AmstelvarAlpha-VF.ttf]
- % \showfontvariations
- % [font=file:bahnschrift.ttf]
+ \showfontvariations
+ [font=file:bahnschrift.ttf]
- % \showfontvariations
- % [font=file:sitka.ttc]
+% \showfontvariations
+% [font=file:sitka.ttc]
\stoptext
diff --git a/tex/context/modules/mkiv/s-present-luatex.mkiv b/tex/context/modules/mkiv/s-present-luatex.mkiv
new file mode 100644
index 000000000..079860bf9
--- /dev/null
+++ b/tex/context/modules/mkiv/s-present-luatex.mkiv
@@ -0,0 +1,118 @@
+%D \module
+%D [ file=s-present-luatex,
+%D version=2016.04.30, % around
+%D title=\CONTEXT\ Style File,
+%D subtitle=Presentation Environment Banner,
+%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 As with older presentations we use the \LUA\ moon as page counter. The rest
+%D of the style is simple because I got bored of all this fancy stepping for
+%D simple presentations.
+
+%D Beware: this presentation is meant for my own use and not a general purpose
+%D presentation style. It is a rather dumb module but served its purpose well.
+
+\startmodule[present-luatex]
+
+\useMPlibrary[lua]
+
+\setupbodyfont
+ [dejavu,17.3pt]
+
+\startuseMPgraphic{page}
+ StartPage ;
+ luaorbitcolor := white ;
+ fill Page withcolor (.5white - luaplanetcolor) ;
+ draw anchored.urt(image(drawluapagenumbergoround) xsized 150pt,urcorner Page shifted (-15pt,-10pt)) ;
+ if RealPageNumber > 1 :
+ begingroup;
+ save p, q; picture p, q;
+ p := textext("\ssbf\setstrut\strut \documentvariable{mp:title}") xsized 150pt ;
+ q := textext("\ssbf\setstrut\strut \documentvariable{mp:subtitle}") xsized 150pt ;
+ setbounds q to boundingbox(p);
+ draw anchored.lrt(p,lrcorner Page shifted (-15pt, 90pt)) withcolor luaplanetcolor ;
+ draw anchored.lrt(q,lrcorner Page shifted (-15pt, 55pt)) withcolor luaholecolor ;
+ endgroup;
+ fi ;
+ fill Field[Text][Text] enlarged 10pt withcolor (.5white - luaplanetcolor)/1.5 ;
+ StopPage ;
+\stopuseMPgraphic
+
+\defineoverlay
+ [page]
+ [\useMPgraphic{page}]
+
+\setupbackgrounds
+ [page]
+ [background=page]
+
+\setuppapersize
+ [S6]
+
+\setuplayout
+ [backspace=50pt,
+ cutspace=200pt,
+ topspace=40pt,
+ bottomspace=40pt,
+ margin=0pt,
+ footer=0pt,
+ header=0pt,
+ width=middle,
+ height=middle]
+
+\setupmakeup
+ [standard]
+ [top=\vfil,
+ bottom=\vfilll,
+ setups=standardmakeup,
+ align=middle,
+ color=white]
+
+\setupcolors
+ [textcolor=white]
+
+\startsetups standardmakeup
+ \setupwhitespace
+ [big]
+\stopsetups
+
+\setupdocument
+ [title=Title,
+ subtitle=Subtitle,
+ location=\currentdate,
+ mp:title=title,
+ mp:subtitle=subtitle]
+
+\startsetups document:start
+
+ \startstandardmakeup[bottom=,top=]
+
+ \raggedcenter
+
+ \vfil\vfil
+ \dontleavehmode\scale[width=1.00\textwidth]{\ssbf\documentvariable{title}}
+ \vfil
+ \dontleavehmode\scale[width=0.75\textwidth]{\ssbf\documentvariable{subtitle}}
+ \vfil
+ \dontleavehmode\scale[width=0.65\textwidth]{\ssbf\documentvariable{location}}
+ \vfil
+ \dontleavehmode\scale[width=0.45\textwidth]{\ssbf\documentvariable{author}}
+ \vfil\vfil\vfil
+
+ \stopstandardmakeup
+
+\stopsetups
+
+\stopmodule
+
+\continueifinputfile{s-present-luatex.mkiv}
+
+\usemodule[present-common]
+
+\inputpresentationfile{context/2016/context-2016-luatex.tex}
diff --git a/tex/context/modules/mkiv/s-tugboat.mkiv b/tex/context/modules/mkiv/s-tugboat.mkiv
index d3d15c3e7..e243dc487 100644
--- a/tex/context/modules/mkiv/s-tugboat.mkiv
+++ b/tex/context/modules/mkiv/s-tugboat.mkiv
@@ -1,32 +1,33 @@
%D \module
%D [ file=t-tugboat
-%D version=$Id: t-tugboat.tex 111 2013-02-25 17:56:22Z bnb $
+%D version=$Id: s-tugboat.mkiv 246 2019-09-09 18:22:19Z karl $
%D title=\CONTEXT\ Style File,
%D subtitle=\TUGBOAT\ base style,
-%D author=Aditya Mahajan,
+%D author=Aditya Mahajan and others,
%D date=\currentdate,
%D copyright=Public Domain]
-%D This file is derived from \filename{s-tug-02} written by Hans Hagen
-%D and Steve Grathwohl. Karl Berry asked me for some changes in the
-%D \TUGBOAT\ style files. The original file had an option of typesetting
-%D on a grid. That was too clever for me to understand, and hence to
-%D modify. Since \TUGBOAT\ usually does not typeset on a grid, I redid the
-%D style, borrowing parts from \filename{s-tug-02} and redoing some
-%D parts from scratch.
+%D \filename {s-tug-02} was originally written by Hans Hagen and Steve Grathwohl,
+%D and used grid typesetting. \filename {t-tugboat} was a major revision by Aditya
+%D Mahajan, no grid. \filename {s-tugboat.mkiv} was another major revision by Taco
+%D Hoekwater and Alan Braslau, now with pagecolumns. The latest version has been
+%D adapted by Karl Berry.
-%D \section Variables %<<<1
+%D HH: most articles are not that demanding, otherwise I'd probably redefine some
+%D of the code below. So, for now things gets adapted when needed.
+
+%D \section{Variables}
%D
%D We store the information about the article in variables.
\setvariables
[tugboat]
[type=article,
- columns=no,
- grid=yes]
+ columns=yes,
+ grid=no]
\setvariables
- [tugboat]
+ [tugboat] % overridden from ../tugboat.dates if present.
[year=1900,
volume=0,
number=0,
@@ -38,45 +39,44 @@
subtitle=,
keywords=,
author=T. Boat,
- address=Pragmatically Advanced tugboats \\
- 314 Pi Ave. \\
- 8061GH Hasselt NL,
- email={tugboat@tug.org}]
+ address=Pragmatically Advanced tugboats\\314 Pi Ave.\\8061GH Hasselt NL,
+ email={pragboat@tug.org}]
-%D \section Font Setup %<<<1
+%D \section{Font Setup}
%D
-%D \TUGBOAT\ uses slightly different interline space than the default.
-%D So we change the interline space.
+%D \TUGBOAT\ uses standard CM interline spacing.
-\definebodyfontenvironment [8pt] [interlinespace=9.5pt, big=9pt, small=7pt]
-\definebodyfontenvironment [9pt] [interlinespace=11pt, big=10pt, small=8pt]
-\definebodyfontenvironment [10pt] [interlinespace=12pt, big=12pt, small=9pt]
-\definebodyfontenvironment [12pt] [interlinespace=14pt,big=14.4pt,small=10pt]
-\definebodyfontenvironment [14.4pt] [interlinespace=18pt,big=14.4pt,small=12pt]
-\definebodyfontenvironment [9.8pt] % Because 9.8pt is used in typing environments
+\definebodyfontenvironment [8pt] [interlinespace=9.5pt, big=9pt, small=7pt]
+\definebodyfontenvironment [9pt] [interlinespace=11pt, big=10pt, small=8pt]
+\definebodyfontenvironment [10pt] [interlinespace=12pt, big=12pt, small=9pt]
+\definebodyfontenvironment [12pt] [interlinespace=14pt, big=14.4pt,small=10pt]
+\definebodyfontenvironment [14.4pt] [interlinespace=18pt, big=14.4pt,small=12pt]
-%D \TUGBOAT\ uses Computer Modern fonts, and \CONTEXT\ uses Latin Modern
-%D by default. So, we just specify the font size.
+%D \TUGBOAT\ uses Computer Modern fonts, and \CONTEXT\ uses Latin Modern by default,
+%D which is fine. So, we just specify the font size.
\setupbodyfont [10pt]
-% Italic rather than slanted for emphasis.
-\setupbodyfontenvironment[default][em=italic]
+%D \TUGBOAT\ uses italic rather than slanted for emphasis.
+
+\setupbodyfontenvironment
+ [default]
+ [em=italic]
+
+%D Break after these chars in urls, not before.
-% Break after these chars in urls, not before.
\sethyphenatedurlafter /
\sethyphenatedurlafter .
\sethyphenatedurlafter _
-%D \section Layout Setup %<<<1
+%D \section{Layout Setup}
%D
-%D The original layout used in the \LATEX\ style for \TUGBOAT\ is a bit
-%D ambiguous. It uses low|-|level \TEX\ syntax, rather than changing the
-%D layout in a human understandable way (for example, by using the
-%D \mono{geometry} package. I have tried to translate it to \CONTEXT\ as
-%D far as I understand.
+%D The original layout used in the \LATEX\ style for \TUGBOAT\ uses low|-|level
+%D \TEX\ syntax.
-\setuppapersize[letter][letter]
+\setuppapersize
+ [letter]
+ [letter]
\setuplayout
[topspace=3.8pc,% was 3.5pc
@@ -95,32 +95,43 @@
margindistance=1pc,
]
-\setupcolumns[distance=1.5pc]
+%D With pagecolumns, seemingly can't get single-column floats. (HH: not sure
+%D what is meant but probably that it can't be done in the running text.)
+
+\enablemode [pagecolumns]
+
+%D KB: With columnsets, get unwanted gridsetting and blank last pages. HH: can
+%D be solved but columnsets normally get set up beforehand (figures and such).
+
+% \enablemode [columnsets]
+% \startmode [columnsets]
+% \usemodule [newcolumnsets]
+% \stopmode
\setuppagenumbering
[location=,
alternative=doublesided]
-%D In \TUGBOAT\ different articles are glued together to form the final
-%D journal, so we do not want each article to occupy even number of
-%D pages.
+%D In \TUGBOAT\ different articles are glued together to form the final journal, so
+%D we do not want each article to occupy an even number of pages. (Except this seems
+%D to have no effect with pagecolumns.) HH: page columns are actually just single
+%D columns but combined, so it supports side floats and such.
\installpagebreakhandler {last} {}
-%D \section Indentation %<<<1
+%D \section{Indentation}
%D
-%D We use automatic indentation control, that is: no
-%D indentation after titles and skips.
+%D We use automatic indentation control, that is: no indentation after titles and
+%D skips.
\setupindenting[20pt,yes]
-%D We do not want indentation after lists.
+%D We do not usually want indentation after lists.
\setupenumerations [indentnext=no]
-
\setupdescriptions [indentnext=no]
-%D \section Itemize
+%D \section{Itemize}
%D
%D And these. We typeset itemizations ragged right.
@@ -144,70 +155,112 @@
[1]
[packed]
-%D \section Section Headings %<<<1
+%D \section{Section Headings}
%D
-%D We follow the \TUGBOAT\ style for sections. I do not know if
-%D \type{align=right} also disables hyphenation. Lets wait and see on
-%D this. Rest all is straight forward. It took me a while to realize
-%D that in \LATEX\ \type{\@startsection} the absolute value of before
-%D skip (fourth argument) is important and not the sign.
+%D We follow the \TUGBOAT\ style for sections. I do not know if \type {align=right}
+%D also disables hyphenation (HH: no that is independent). Let's wait and see on
+%D this. The rest is straightforward. It took me a while to realize that in \LATEX\
+%D \type {\@startsection} the absolute value of before skip (fourth argument) is
+%D important and not the sign.
\setuphead
- [section,subsection, subsubsection,
- subject, subsubject, subsubsubject]
+ [section,subsection,subsubsection,
+ subject,subsubject,subsubsubject]
[style=bold,
align=right,
before={\blank[8pt]},
after={\blank[4pt]}]
-%D \section Spacing <<<1
+%D \section{Spacing}
%D
-%D We define a logical skip. This is equal to the \tex{topsep} in latex,
-%D and most environments should have this skip.
+%D We define a logical skip. This is equal to the \type {\topsep} in \LATEX, and
+%D most environments should have this skip.
+
+\defineblank
+ [tugblank]
+ [3pt]
-\defineblank[tugblank][3pt]
-\setupblank[3pt]
+%D HH: In fact, \type \setupblank[tugblank] would deal with most of the blanks
+%D anyway, but let's not change this now.
+\setupblank
+ [3pt]
-\setupitemize[1][before={\blank[tugblank]},after={\blank[tugblank]},
- inbetween={\blank[tugblank]}]
+\setupitemize
+ [1]
+ [before={\blank[tugblank]},
+ after={\blank[tugblank]},
+ inbetween={\blank[tugblank]}]
-\setuplines[before={\blank[tugblank]},after={\blank[tugblank]},
- inbetween={\blank[tugblank]}]
+\setuplines
+ [before={\blank[tugblank]},
+ after={\blank[tugblank]},
+ inbetween={\blank[tugblank]}]
%D \section Typing %<<<1
%D
-%D \TUGBOAT\ uses a smaller font size for verbatim typesetting.
+%D \TUGBOAT\ uses a smaller font size for verbatim typesetting. Not sure why
+%D \type {nowhite} is used here.
\setuptyping
[option=none,
- % mkiv arbitrarily scales lm somehow randomly; 10pt gets 9pt back,
- % we make it a little smaller.
- bodyfont=9.8pt,
- before={\blank[nowhite]}, % AM: If this is too small, use \blank[nowhite,tugblank]}
+ bodyfont=9pt,
+ before={\blank[nowhite,tugblank]},
after={\blank[back,nowhite,tugblank]}]
-%D \section Footnotes %<<<1
+%D Do not colorize code.
+
+\setuptyping[LUA][option=]
+\setuptyping[TEX][option=]
+\setuptyping[MP] [option=]
+
+%D Tiny line numbers in numbered code (not text size)
+
+\setuplinenumbering[style=\tfxx] % \infofont would be really tiny
+
+%D \section{Figure captions}
+%D
+%D \TUGBOAT\ uses a smaller font size for captions, too; \type {\small} gives 8pt,
+%D and we want 9pt, so we have to manually fix the baselines.
+
+\setupcaptions
+ [headstyle=\AcroFontBold,
+ style={\AcroFont\advance\baselineskip by -1pt},
+ align=right]
+
+%D We like a colon after the caption label.
+
+\let\floatcaptionsuffix=:
+
+% Prefer floats in text.
+
+\setupfloat[figure][align=middle,default=here]
+\setupfloat[table] [align=middle,default=here]
+
+%D \section{Footnotes}
%D
%D Not entirely a la \TUGBOAT:
+% rule={\hrule width 5pc height .4pt depth 0pt\relax \kern \strutdepth},
+
\setupfootnotes
- [bodyfont=9pt,
- location=columns%,
-% rule={\hrule width 5pc height .4pt depth 0pt\relax \kern \strutdepth}
-]
+ [bodyfont=9pt]
+
+\doif {\getvariable{tugboat}{columns}} {yes} {
+ \setupfootnotes
+ [location=columns]
+}
-\setupnotation[footnote]
+\setupnotation
+ [footnote]
[location=joinedup,
width=fit,
headstyle=normal,
distance=.5em]
-%>>>
-%D \section List %<<<
+%D \section{List}
%D
-%D We define a standard description and enumeration
-%D environment.
+%D We define a standard description and enumeration environment.
\definedescription
[description]
@@ -223,31 +276,26 @@
before={\blank[tugblank]},
after={\blank[tugblank]}]
-
-% >>>
-%D \section References %<<<
+%D \section{References}
%D
-%D The bib does not handle urls nicely. So we provide a stop gap solution.
-
-\definereferenceformat[cite][left={[},right={]}]
-\defineitemgroup [bibliography] [levels=1]
-\setupitemgroup [bibliography]
- [symbol=n,
- left={[},
- right={]},
- width=1.5em,
- stopper=,
- itemalign=flushright,
- inbetween={\blank[small]}]
+%D More or less like plain BibTeX + ltugboat.
+
+\setupbtx
+ [default:list:numbering]
+ [left={[},
+ right={]}]
+
+\setupbtxlist
+ [default]
+ [width=2em,
+ style={\frenchspacing},
+ align={raggedright,verystrict}]
%D Instead of color, we use weighted gray scales:
+
\setupcolors
[conversion=always]
-%D English it is.
-\mainlanguage
- [en]
-
%D We define some logical skips
\defineblank [tugbefore] [big]
@@ -261,26 +309,28 @@
\startsetups tugboat:abstract:setup
- \setuptolerance
- [horizontal, tolerant]
+ % \setuptolerance
+ % [horizontal, tolerant]
+
+ \setupnarrower
+ [before={\blank[.5\baselineskip]},
+ % after={\blank[10pt plus4pt minus4pt]},
+ after={\blank[10pt]},
+ middle=4.875pc]
- \setupnarrower
- [before={\blank[.5\baselineskip]},
-% after={\blank[10pt plus4pt minus4pt]},
- after={\blank[10pt]},
- middle=4.875pc]
+ \setupindenting
+ [20pt,yes]
\stopsetups
-%D Headers and footers are different for normal issues
-%D and proceedings.
+%D Headers and footers.
\startsetups tugboat:banner:text:article
- {\sl TUGboat},\space
- Volume \getvariable{tugboat}{volume}\space
- (\getvariable{tugboat}{year}),\space
- No.\space\getvariable{tugboat}{number}
+ {\sl TUGboat},\space
+ Volume \getvariable{tugboat}{volume}\space
+ (\getvariable{tugboat}{year}),\space
+ No.\space\getvariable{tugboat}{number}
\stopsetups
@@ -288,9 +338,9 @@
\startsetups tugboat:banner:text:proceedings
- \setups{tugboat:banner:text:article}
- \thinspace---\thinspace
- \postissno
+ \setups{tugboat:banner:text:article}
+ \thinspace---\thinspace
+ \postissno
\stopsetups
@@ -300,26 +350,15 @@
[\setups{tugboat:banner:text:article}]
[\pagenumber]
-% no footer in regular articles
-% \setupfootertexts
-% [][\getvariable{tugboat}{author}]
-% [\getvariable{tugboat}{title}][]
-
-\stopsetups
-
-\startsetups tugboat:banner:setup:proceedings
-
- \setupheadertexts
- [][\getvariable{tugboat}{title}]
- [\getvariable{tugboat}{author}][]
+ % no footer in regular articles
\setupfootertexts
- [\setups{tugboat:banner:text:proceedings}]
- [\pagenumber]
+ [][\getvariable{tugboat}{author}]
+ [\getvariable{tugboat}{title}][]
\stopsetups
-%D article is default so,
+%D article is default.
\setups{tugboat:banner:setup:article}
@@ -330,333 +369,334 @@
\startsetups tugboat:grid:setup:settings:yes
- \setupblank
- [line]
+ \setupblank
+ [line]
- \defineblank [tugbefore] [halfline]
- \defineblank [tuginbetween] [halfline]
+ \defineblank [tugbefore] [halfline]
+ \defineblank [tuginbetween] [halfline]
\stopsetups
-\startsetups tugboat:grid:setup:no
-
+% \startsetups tugboat:grid:setup:no
+%
% \setupblank
% [halfline]
-
+%
% \defineblank [medium] [halfline]
% \defineblank [tugbefore] [halfline]
% \defineblank [tuginbetween] [halfline]
-
-\stopsetups
+%
+% \stopsetups
\startsetups tugboat:introduction:article
-% \blank[halfline]
-
- \start
- \def\\{\unskip\space\&\space\ignorespaces}
- \hbox{\indent\getvariable{tugboat}{author}}
- \par
- \stop
-
-
-\stopsetups
-
-\startsetups tugboat:introduction:proceedings
-
- \blank[20pt]
-
- \start
- \switchtobodyfont[12pt]
- \def\\{\unskip\space\&\space\ignorespaces}
- \getvariable{tugboat}{author}
- \par
- \stop
+ % \blank[halfline]
\start
- \switchtobodyfont[9pt]
- \def\\{\unskip,\space\ignorespaces}
- \getvariable{tugboat}{address}
- \par
- {\tt\getvariable{tugboat}{email}}
- \par
- \stop
-
+ \def\\{\unskip\space\&\space\ignorespaces}
+ \hbox{\indent\getvariable{tugboat}{author}}
+ \par
+ \stop
\stopsetups
\startsetups tugboat:article:start
- \starttext
-
- \setups{tugboat:columns:\getvariable{tugboat}{columns}}
- \setups{tugboat:banner:setup:\getvariable{tugboat}{type}}
-
- \doif{\getvariable{tugboat}{columns}}{yes}{\startcolumns}
+ \starttext
+
+ \setups{tugboat:columns:\getvariable{tugboat}{columns}}
+ \setups{tugboat:banner:setup:\getvariable{tugboat}{type}}
+
+ \doif {\getvariable{tugboat}{columns}} {yes} {
+ \doifmodeelse {columnset} {
+ \startcolumnset
+ } {
+ \doifmodeelse {pagecolumns} {
+ \startpagecolumns
+ } {
+ \startcolumns
+ }
+ }
+ }
+
+ \setcounter
+ [userpage]
+ [\getvariable{tugboat}{page}]
+
+ \setups{tugboat:grid:setup:\getvariable{tugboat}{grid}}
+
+ \snaptogrid \vbox \bgroup
+
+ \forgetall
+ \hrule height .6pt
+ \blank[halfline]
+ \start
+ \let\\=\par
+ {\bf\getvariable{tugboat}{title}}
+ \par
+ \blank[halfline]
+ \hskip20pt\getvariable{tugboat}{author}
+ \stop
+
+ \blank[line]
+
+ \doiftext {\getbuffer[abstract]} {
+ \let\\=\endgraf
+ \setups{tugboat:abstract:setup}
+ \subject{Abstract}
+ \getbuffer[abstract]
+ }
+
+ \egroup
+\stopsetups
- % AM: Why set these again?
- % \setupheadertexts
- % [\setups{tugboat:banner:text:article}]
- % [pagenumber]
-
- % \setuppagenumber
- % [number=\getvariable{tugboat}{page}]
- \setcounter[userpage][\getvariable{tugboat}{page}]
-
- % \setuplayout
- % [grid=\getvariable{tugboat}{grid}]
-
- % % instead of \startmode [*grid] ... \stopmode
- % % instead of \startnotmode[*grid] ... \stopnotmode
-
- \setups{tugboat:grid:setup:\getvariable{tugboat}{grid}}
-
- \snaptogrid \vbox \bgroup
-
- \forgetall
-\hrule height .6pt
-\blank[halfline]
- \start
- \let\\=\par
- {\bf\getvariable{tugboat}{title}}
- \par
- \blank[halfline]
- \hskip20pt\getvariable{tugboat}{author}
- \stop
-
-% \setups{tugboat:introduction:\getvariable{tugboat}{type}}
+\startsetups tugboat:affiliation:article
\blank[line]
+ \begingroup
+ \leftskip=5.7pc
+ \noindent
+ \llap{\sym{\diamond}\enspace}
+ \getvariable{tugboat}{author}
+ \\
+ \getvariable{tugboat}{address}
+ \\
+ {\tt\getvariable{tugboat}{email}}
+ \par
+ \endgroup
- \doiftext{\getbuffer[abstract]}
- {\let\\=\endgraf
- \setups{tugboat:abstract:setup}
- \subject{Abstract}
- \getbuffer[abstract]}
- \egroup
\stopsetups
-\startsetups tugboat:proceedings:start
-
- \starttext
-
- \setups{tugboat:columns:\getvariable{tugboat}{columns}}
- \setups{tugboat:banner:setup:\getvariable{tugboat}{type}}
-
- \setupheader
- [state=empty]
-
- \setuppagenumber
- [number=\getvariable{tugboat}{page}]
-
- \setuplayout
- [grid=\getvariable{tugboat}{grid}]
-
- % instead of \startmode [*grid] ... \stopmode
- % instead of \startnotmode[*grid] ... \stopnotmode
- \doifmodeelse{*grid}{\setups{tugboat:grid:setup:yes}}{\setups{tugboat:grid:setup:no}}
-% \setups{tugboat:grid:setup:\systemmodevalue{grid}{yes}{no}}
-
- \snaptogrid \vbox \bgroup
-
- \forgetall
-
- \start
- \switchtobodyfont[14.4pt]
- \let\\=\par
- \getvariable{tugboat}{title}
- \par
- \stop
-
- \setups{tugboat:introduction:\getvariable{tugboat}{type}}
-
- \blank[9pt]% plus3pt minus3pt]
-
- \let\\=\par
- \setups{tugboat:abstract:setup}
- \midaligned{\bf Abstract}
+\startsetups tugboat:article:stop
- \startnarrower[middle]
- \getbuffer[abstract]
- \stopnarrower
+ \setups{tugboat:affiliation:\getvariable{tugboat}{type}}
- % \blank[10pt plus4pt minus4pt]
+ % article joining in the issue.
- \egroup
+ \ifx\writelastpage\undefined \else
+ \begingroup
+ \count0=\count1 % because context uses \count0
+ \writelastpage{+1}%
+ \endgroup
+ \fi
- \doif{\getvariable{tugboat}{columns}}{yes}{\startcolumns}
+ \stoptext
\stopsetups
-\def\signaturewidth{13pc}
+%D Normal word spacing, please.
-\definefontsynonym[LMSY][lmsy10]
-\def\mydiamond{\getglyph{LMSY}{\char5}}
+\setuptolerance
+ [strict]
-\startsetups tugboat:affiliation:article
+%D Hz is fine but hanging can hang quotes out too far.
- \blank[line]
+\setupalign[hz]
- \snaptogrid \vbox \bgroup
+%D Let's see boxes overfull by >.5pt
- \forgetall
+\overfullrule = 2pt % yes, I want to see
+\hfuzz = .5pt
- \leftskip=\textwidth \advance\leftskip by -\signaturewidth
+%D One can use the following setups (in the article) to collect settings specific to
+%D normal and|/|or multi column typesetting.
- \let\\=\par
- \leavevmode\llap{\mydiamond\enspace}\getvariable{tugboat}{author}
- \par
- \getvariable{tugboat}{address}
- \par
- {\tt\getvariable{tugboat}{email}}
+\startsetups tugboat:columns:yes
- \egroup
+ \doifmodeelse {columnset} {
+ \setuplayout
+ [grid=no]
+ \setupcolumnset
+ [distance=1.5pc]
+ } {
+ \doifmodeelse {pagecolumns} {
+ \setuplayout
+ [grid=no]
+ \setuppagecolumns
+ [distance=1.5pc]
+ } {
+ \setupcolumns
+ [distance=1.5pc]
+ }
+ }
\stopsetups
-\startsetups tugboat:affiliation:proceedings
-
- % nothing fancy at the end
-
-\stopsetups
+% \startsetups tugboat:columns:no
+%
+% \stopsetups
-\startsetups tugboat:article:stop
+%D Logos, abbreviations: lots could be better here.
- \setups{tugboat:affiliation:\getvariable{tugboat}{type}}
+\font\mflogo=logo10 % hm, better use \definefont
- \doif{\getvariable{tugboat}{columns}}{yes}{\stopcolumns}
+%D HH: I'm pretty sure that this doesn't really work as it probably assumes some
+%D specific math family setup and that's not how \MKIV\ works. The TB definition
+%D doesn't scale with font changes and also doesn't adapt itself to style changes.
- \page
+\newcount\TestCount
+\newbox \TestBox
+
+\def\La
+ {\dontleavehmode
+ \begingroup
+ \TestCount=\the\fam % no meaningful fams in text mode in ConTeXt
+ \setbox\TestBox=\hbox{$\fam\TestCount\scriptstyle A$}%
+ L\kern-.5\wd\TestBox\raise.42ex\box\TestBox
+ \endgroup}
+
+%D Probably this will do a better job:
+
+% \def\La
+% {\dontleavehmode
+% \begingroup
+% \setbox\scratchbox\hbox{\tx A}% % or \txx
+% L\kern-.5\wd\scratchbox\raise.42ex\scratchbox
+% \endgroup}
+
+\def\ALEPH {Aleph}
+\def\API {\acro{API}}
+\def\CCODE {C}
+\def\CD {\acro{CD}}
+\def\CMYK {\acro{CMYK}}
+\def\CONTEXT {C\kern-.0333emon\-\kern-.0667em\TeX\kern-.0333emt}
+\def\Cplusplus {C\plusplus}
+\def\CPU {\acro{CPU}}
+\def\DVI {\acro{DVI}}
+\def\DVIPDFMX {dvipdfmx}
+\def\DVIPS {dvips}
+\def\ETEX {$\varepsilon$-\kern-.125em\TeX}
+\def\FTP {\acro{FTP}}
+\def\HTTP {\acro{HTTP}}
+\def\IO {\acro{I/O}}
+\def\ISO {\acro{ISO}}
+\def\KPSE {\acro{KPSE}}
+\def\KPSEWHICH {kpsewhich}
+\def\LATEX {\La\kern-.15em\TeX}
+\def\LATEXE {\LaTeX{}\kern.05em2$_{\textstyle\varepsilon}$}
+\def\LINUX {Linux}
+\def\LPEG {Lpeg}
+\def\LUA {Lua}
+\def\LUAJIT {Lua\acro{JIT}}
+\def\LUATEX {Lua\-\TeX}
+\def\LUATOOLS {lua\-tools}
+\def\MATHML {Math\acro{ML}}
+\def\METAFUN {Metafun}
+\def\METAPOST {MetaPost}
+\def\METATEX {Meta\TeX{}}
+\def\MF {{\mflogo META\-FONT}}
+\def\MKII {Mk\acro{II}}
+\def\MKIV {Mk\acro{IV}}
+\def\MPLIB {\acro{MP}lib}
+\def\MPTOPDF {mp\-to\-pdf}
+\def\MSWINDOWS {Windows}
+\def\MTXTOOLS {mtx\-tools}
+\def\NFSS {\acro{NFSS}}
+\def\OPENMATH {Open\-Math}
+\def\OPENTYPE {Open\-Type}
+\def\PASCAL {Pascal}
+\def\PDF {\acro{PDF}}
+\def\PDFTEX {pdf\/\-\TeX}
+\def\plusplus {\lower.45ex\hbox{$^{++}$}}
+\def\POSIX {\acro{POSIX}}
+\def\POSTSCRIPT{Post\-Script}
+\def\PRAGMA {Pragma \acro{ADE}}
+\def\RGB {\acro{RGB}}
+\def\RUBY {Ruby}
+\def\SCITE {Sci\acro{TE}}
+\def\TDS {\acro{TDS}}
+\def\TEX {\TeX}
+\def\TEXBOOK {{\sl The \TeX book}}
+\def\TEXEXEC {\TeX exec}
+\def\TFM {\acro{TFM}}
+\def\TRUETYPE {True\-Type}
+\def\TYPEONE {Type~1}
+\def\UTF {\acro{UTF}}
+\def\WEBC {Web2C}
+\def\XETEX {X\lower.5ex\hbox{\kern-.15em\mirror{E}}\kern-.1667em\TeX}
+\def\XML {\acro{XML}}
+\def\XPATH {\acro{XP}ath}
+\def\XSLT {\acro{XSLT}}
+\def\XSLTPROC {\acro{XSLTPROC}}
+\def\ZIP {zip}
+
+\def\Dash {\unskip\nobreak\thinspace---\thinspace\ignorespaces}
+\def\slash {/\penalty0 \hskip0pt \relax}
+
+% \acro for one point size smaller. Don't know how to do this for real.
+
+\definefont[AcroFont] [Serif*default sa .91]
+\definefont[AcroFontBold] [SerifBold*default sa .91]
+\definefont[AcroFontSmall][Serif*default sa .80]
+
+\unexpanded\def\acro #1{{\AcroFont #1}} % could be a style definition
+\unexpanded\def\acrobf #1{{\AcroFontBold #1}} % could be a style definition
+\unexpanded\def\acrosmall#1{{\AcroFontSmall #1}} % could be a style definition
+
+%D This is English; disallow x- or -xx breaks. (These are already the defaults
+%D but let's keep this anyway.)
+
+\setuplanguage
+ [en]
+ [lefthyphenmin=2,
+ righthyphenmin=3]
- \stoptext
+% \lefthyphenmin = 2
+% \righthyphenmin = 3
-\stopsetups
+\mainlanguage
+ [en]
-%D Normal word spacing, please.
+\hyphenation {
+ Post-Script
+ data-base
+ data-bases
+}
-\setuptolerance
- [strict]
+%D A hack to read \type {tugboat.dates} settings.
-%D One can use the following setups (in the article) to
-%D collect settings specific to normal and/or multi
-%D column typesetting.
+\doifelsefileexists {../tugboat.dates} {
-\startsetups tugboat:columns:yes
+ \newif \ifPrelimDraft
+ \newcount\issueseqno
-\stopsetups
+ \def\vol #1, #2.{\gdef\tubvol {#1}%
+ \gdef\tubnum {#2}}
+ \def\issyear #1.{\gdef\tubyear{#1}}
-\startsetups tugboat:columns:no
+ \input ../tugboat.dates
-\stopsetups
+ \setevariables
+ [tugboat]
+ [year=\tubyear,
+ volume=\tubvol,
+ number=\tubnum,
+ page=\the\count0]
-% >>> Logos, abbreviations: TODO: Clean up <<<
-\font\mflogo = logo10
-\def\MF{{\mflogo META\-FONT}}
-
-\def\ALEPH{Aleph}
-\def\API{\acro{API}}
-\def\CCODE{C}
-\def\CD{\acro{CD}}
-\def\CMYK{\acro{CMYK}}
-\def\CONTEXT{C\kern-.0333emon\-\kern-.0667em\TeX\kern-.0333emt}
-\def\CPU{\acro{CPU}}
-\def\DVI{\acro{DVI}}
-\def\DVIPDFMX{dvipdfmx}
-\def\DVIPS{dvips}
-\def\ETEX{$\varepsilon$-\kern-.125em\TeX}
-\def\FTP{\acro{FTP}}
-\def\HTTP{\acro{HTTP}}
-\def\IO{\acro{I/O}}
-\def\ISO{\acro{ISO}}
-\def\KPSEWHICH{kpsewhich}
-\def\KPSE{\acro{KPSE}}
-\newcount\TestCount
-\newbox\TestBox
-\def\La{\TestCount=\the\fam \leavevmode L%
- \setbox\TestBox=\hbox{$\fam\TestCount\scriptstyle A$}%
- \kern-.5\wd\TestBox\raise.42ex\box\TestBox}
-\def\LATEX{\La\kern-.15em\TeX}
-\def\LATEXE{\LaTeX{}\kern.05em2$_{\textstyle\varepsilon}$}
-\def\LINUX{Linux}
-\def\LPEG{Lpeg}
-\def\LUAJIT{Lua\acro{JIT}}
-\def\LUATEX{Lua\-\TeX}
-\def\LUATOOLS{lua\-tools}
-\def\LUA{Lua}
-\def\MATHML{Math\acro{ML}}
-\def\METAFUN{Metafun}
-\def\METAPOST{MetaPost}
-\def\METATEX{Meta\TeX{}}
-\def\MKII{Mk\acro{II}}
-\def\MKIV{Mk\acro{IV}}
-\def\MPLIB{\acro{MP}lib}
-\def\MPTOPDF{mp\-to\-pdf}
-\def\MSWINDOWS{Windows}
-\def\MTXTOOLS{mtx\-tools}
-\def\NFSS{\acro{NFSS}}
-\def\OPENMATH{Open\-Math}
-\def\OPENTYPE{Open\-Type}
-\def\PASCAL{Pascal}
-\def\PDFTEX{pdf\/\-\TeX}
-\def\PDF{\acro{PDF}}
-\def\POSIX{\acro{POSIX}}
-\def\PRAGMA{Pragma \acro{ADE}}
-\def\POSTSCRIPT{Post\-Script}
-\def\RGB{\acro{RGB}}
-\def\RUBY{Ruby}
-\def\SCITE{Sci\acro{TE}}
-\def\TDS{\acro{TDS}}
-\def\TEXBOOK{{\sl The \TeX book}}
-\def\TEXEXEC{\TeX exec}
-\def\TEX{\TeX}
-\def\TFM{\acro{TFM}}
-\def\TRUETYPE{True\-Type}
-\def\TYPEONE{Type~1}
-\def\UTF{\acro{UTF}}
-\def\WEBC{Web2C}
-\def\XETEX{X\lower.5ex\hbox{\kern-.15em\mirror{E}}\kern-.1667em\TeX}
-\def\XML{\acro{XML}}
-\def\XPATH{\acro{XP}ath}
-\def\XSLT{\acro{XSLT}}
-\def\XSLTPROC{\acro{XSLTPROC}}
-\def\ZIP{zip}
-
-\def\Dash{\unskip\thinspace---\thinspace\ignorespaces}
-\def\slash{/\penalty0 \hskip0pt \relax}
-
-\definefont[AcroFont][Serif sa .91]
-\def\acro#1{{\AcroFont #1}}
-
-\lefthyphenmin=2 \righthyphenmin=3 % disallow x- or -xx breaks
-
-\hyphenation{Post-Script data-base data-bases}
-
-% hack to read tugboat.dates settings.
-\def\vol#1, #2.{\def\tubvol{#1}\def\tubnum{#2}}
-\def\issyear#1.{\def\tubyear{#1}}
-\newcount\issueseqno
+} {
+ % nothing
+}
-% >>>
-%D Good bye. <<<
+%D Good bye.
-\doifnotmode{demo}{\endinput}
+\continueifinputfile{s-tugboat.mkiv}
-\showgrid
+% \showgrid
\starttext
\StartArticle
\StartAbstract
- \input bryson
+ \samplefile{bryson}
\StopAbstract
\dorecurse{30}{\input ward \endgraf} \page
\startitemize
-\item \input ward
-\item \input ward
+ \startitem \samplefile{ward} \stopitem
+ \startitem \samplefile{ward} \stopitem
\stopitemize
\StopArticle
@@ -666,15 +706,15 @@
\StartArticle
\StartAbstract
- \input bryson
+ \samplefile{bryson}
\StopAbstract
-\dorecurse{30}{\input ward \endgraf} \page
+\dorecurse {30} {
+ \samplefile{ward}\endgraf
+} \page
\StopArticle
\stoptext
% >>>
-
-% vim: foldmethod=marker foldmarker=<<<,>>>
diff --git a/tex/generic/context/luatex/luatex-basics-nod.lua b/tex/generic/context/luatex/luatex-basics-nod.lua
index f2f884abf..172fcc7f7 100644
--- a/tex/generic/context/luatex/luatex-basics-nod.lua
+++ b/tex/generic/context/luatex/luatex-basics-nod.lua
@@ -279,6 +279,3 @@ if not nuts.setreplace then
end
end
-
-
-end
diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua
index 47bfe470f..b538d1f3b 100644
--- a/tex/generic/context/luatex/luatex-fonts-merged.lua
+++ b/tex/generic/context/luatex/luatex-fonts-merged.lua
@@ -1,6 +1,6 @@
-- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua
-- parent file : c:/data/develop/context/sources/luatex-fonts.lua
--- merge date : 09/10/19 20:03:16
+-- merge date : 09/27/19 17:59:57
do -- begin closure to overcome local limits and interference
@@ -5024,7 +5024,6 @@ if not nuts.setreplace then
setfield(n,"replace",h)
end
end
-end
end -- closure
@@ -9016,7 +9015,8 @@ function constructors.scale(tfmdata,specification)
local units=parameters.units or 1000
targetproperties.language=properties.language or "dflt"
targetproperties.script=properties.script or "dflt"
- targetproperties.mode=properties.mode or "base"
+ targetproperties.mode=properties.mode or "base"
+ targetproperties.method=properties.method
local askedscaledpoints=scaledpoints
local scaledpoints,delta=constructors.calculatescale(tfmdata,scaledpoints,nil,specification)
local hdelta=delta
@@ -23081,6 +23081,7 @@ otf.cache=containers.define("fonts","otl",otf.version,true)
otf.svgcache=containers.define("fonts","svg",otf.version,true)
otf.pngcache=containers.define("fonts","png",otf.version,true)
otf.pdfcache=containers.define("fonts","pdf",otf.version,true)
+otf.mpscache=containers.define("fonts","mps",otf.version,true)
otf.svgenabled=false
otf.pngenabled=false
local otfreaders=otf.readers
@@ -32098,6 +32099,9 @@ if not modules then modules={} end modules ['font-ocl']={
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
+if CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 then
+ return
+end
local tostring,tonumber,next=tostring,tonumber,next
local round,max=math.round,math.round
local gsub,find=string.gsub,string.find
@@ -32105,7 +32109,6 @@ local sortedkeys,sortedhash,concat=table.sortedkeys,table.sortedhash,table.conca
local setmetatableindex=table.setmetatableindex
local formatters=string.formatters
local tounicode=fonts.mappings.tounicode
-local bpfactor=number.dimenfactors.bp
local helpers=fonts.helpers
local charcommand=helpers.commands.char
local rightcommand=helpers.commands.right
@@ -32292,14 +32295,20 @@ local function pdftovirtual(tfmdata,pdfshapes,kind)
local data=nil
local dx=nil
local dy=nil
+ local scale=1
if typ=="table" then
data=pdf.data
dx=pdf.x or pdf.dx or 0
dy=pdf.y or pdf.dy or 0
+ scale=pdf.scale or 1
elseif typ=="string" then
data=pdf
dx=0
dy=0
+ elseif typ=="number" then
+ data=pdf
+ dx=0
+ dy=0
end
if data then
local bt=unicode and getactualtext(unicode)
@@ -32310,7 +32319,7 @@ local function pdftovirtual(tfmdata,pdfshapes,kind)
not unicode and actualb or { "pdf","page",(getactualtext(unicode)) },
downcommand [dp+dy*hfactor],
rightcommand[ dx*hfactor],
- vfimage(wd,ht,dp,data,name),
+ vfimage(scale*wd,ht,dp,data,pdfshapes.filename or ""),
actuale,
}
character[kind]=true
@@ -32349,7 +32358,7 @@ do
}
if not runner then
runner=function()
- return io.open("inkscape --export-area-drawing --shell > temp-otf-svg-shape.log","w")
+ return io.popen("inkscape --export-area-drawing --shell > temp-otf-svg-shape.log","w")
end
end
function otfsvg.topdf(svgshapes,tfmdata)
@@ -32364,6 +32373,7 @@ do
local f_convert=formatters["%s --export-pdf=%s\n"]
local filterglyph=otfsvg.filterglyph
local nofdone=0
+ local processed={}
report_svg("processing %i svg containers",nofshapes)
statistics.starttiming()
for i=1,nofshapes do
@@ -32375,26 +32385,51 @@ do
local pdffile=f_pdffile(index)
savedata(svgfile,data)
inkscape:write(f_convert(svgfile,pdffile))
- pdfshapes[index]=true
+ processed[index]=true
nofdone=nofdone+1
- if nofdone%100==0 then
- report_svg("%i shapes processed",nofdone)
+ if nofdone%25==0 then
+ report_svg("%i shapes submitted",nofdone)
end
end
end
end
+ if nofdone%25~=0 then
+ report_svg("%i shapes submitted",nofdone)
+ end
+ report_svg("processing can be going on for a while")
inkscape:write("quit\n")
inkscape:close()
report_svg("processing %i pdf results",nofshapes)
- for index in next,pdfshapes do
+ for index in next,processed do
local svgfile=f_svgfile(index)
local pdffile=f_pdffile(index)
- pdfshapes[index]={
- data=loaddata(pdffile),
- }
+ local pdfdata=loaddata(pdffile)
+ if pdfdata and pdfdata~="" then
+ pdfshapes[index]={
+ data=pdfdata,
+ }
+ end
remove(svgfile)
remove(pdffile)
end
+local characters=tfmdata.characters
+for k,v in next,characters do
+ local d=descriptions[k]
+ local i=d.index
+ if i then
+ local p=pdfshapes[i]
+ if p then
+ local w=d.width
+ local l=d.boundingbox[1]
+ local r=d.boundingbox[3]
+ p.scale=(r-l)/w
+ p.x=l
+ end
+ end
+end
+ if not next(pdfshapes) then
+ report_svg("there are no converted shapes, fix your setup")
+ end
statistics.stoptiming()
if statistics.elapsedseconds then
report_svg("svg conversion time %s",statistics.elapsedseconds() or "-")
@@ -32413,10 +32448,10 @@ local function initializesvg(tfmdata,kind,value)
end
local pdffile=containers.read(otf.pdfcache,hash)
local pdfshapes=pdffile and pdffile.pdfshapes
- if not pdfshapes or pdffile.timestamp~=timestamp then
+ if not pdfshapes or pdffile.timestamp~=timestamp or not next(pdfshapes) then
local svgfile=containers.read(otf.svgcache,hash)
local svgshapes=svgfile and svgfile.svgshapes
- pdfshapes=svgshapes and otfsvg.topdf(svgshapes,tfmdata) or {}
+ pdfshapes=svgshapes and otfsvg.topdf(svgshapes,tfmdata,otf.pdfcache.writable,hash) or {}
containers.write(otf.pdfcache,hash,{
pdfshapes=pdfshapes,
timestamp=timestamp,