From 0a5f59a9aa25b3de7e9659b39ad201aaf7eb5a67 Mon Sep 17 00:00:00 2001 From: Hans Hagen Date: Fri, 27 Sep 2019 20:24:34 +0200 Subject: 2019-09-27 18:10:00 --- .../lexers/data/scite-context-data-metafun.lua | 2 +- .../lexers/data/scite-context-data-metapost.lua | 2 +- .../context/scite-context-data-metafun.properties | 4 +- .../context/scite-context-data-metapost.properties | 2 +- .../context/data/scite-context-data-metafun.lua | 2 +- .../context/data/scite-context-data-metapost.lua | 2 +- .../context/syntaxes/context-syntax-mps.json | 4 +- .../documents/general/magazines/mag-1104-mkiv.pdf | Bin 781988 -> 751131 bytes .../documents/general/manuals/lowlevel-boxes.pdf | Bin 0 -> 75834 bytes .../general/manuals/lowlevel-conditionals.pdf | Bin 0 -> 95412 bytes .../general/manuals/lowlevel-expansion.pdf | Bin 0 -> 48778 bytes .../general/manuals/lowlevel-registers.pdf | Bin 0 -> 43435 bytes .../documents/general/manuals/luametafun.pdf | Bin 0 -> 1514692 bytes doc/context/documents/general/qrcs/setup-cs.pdf | Bin 887551 -> 887655 bytes doc/context/documents/general/qrcs/setup-de.pdf | Bin 889628 -> 889580 bytes doc/context/documents/general/qrcs/setup-en.pdf | Bin 893466 -> 893435 bytes doc/context/documents/general/qrcs/setup-fr.pdf | Bin 885546 -> 885603 bytes doc/context/documents/general/qrcs/setup-it.pdf | Bin 891211 -> 891252 bytes .../documents/general/qrcs/setup-mapping-cs.pdf | Bin 383751 -> 383751 bytes .../documents/general/qrcs/setup-mapping-de.pdf | Bin 471626 -> 471430 bytes .../documents/general/qrcs/setup-mapping-en.pdf | Bin 380397 -> 380397 bytes .../documents/general/qrcs/setup-mapping-fr.pdf | Bin 384093 -> 384093 bytes .../documents/general/qrcs/setup-mapping-it.pdf | Bin 383246 -> 383246 bytes .../documents/general/qrcs/setup-mapping-nl.pdf | Bin 382226 -> 382225 bytes .../documents/general/qrcs/setup-mapping-ro.pdf | Bin 620292 -> 620289 bytes doc/context/documents/general/qrcs/setup-nl.pdf | Bin 881171 -> 881208 bytes doc/context/documents/general/qrcs/setup-ro.pdf | Bin 885510 -> 885570 bytes .../context/2017/context-2017-css-selectors.pdf | Bin 0 -> 130327 bytes .../context/2017/context-2017-css-selectors.tex | 238 +++ .../2017/context-2017-features-chaintest.pdf | Bin 0 -> 15666 bytes .../2017/context-2017-features-chaintest.tex | 49 + .../2017/context-2017-features-kerntest.pdf | Bin 0 -> 15259 bytes .../2017/context-2017-features-kerntest.tex | 25 + .../2017/context-2017-features-pairtest.pdf | Bin 0 -> 15703 bytes .../2017/context-2017-features-pairtest.tex | 26 + .../2017/context-2017-features-singletest.pdf | Bin 0 -> 15672 bytes .../2017/context-2017-features-singletest.tex | 25 + .../2017/context-2017-features-spacetest.pdf | Bin 0 -> 15716 bytes .../2017/context-2017-features-spacetest.tex | 43 + .../context-2017-features-substitutiontest.pdf | Bin 0 -> 17567 bytes .../context-2017-features-substitutiontest.tex | 141 ++ .../context/2017/context-2017-features.pdf | Bin 0 -> 58197 bytes .../context/2017/context-2017-features.tex | 153 ++ .../context/2017/context-2017-performance.pdf | Bin 0 -> 51156 bytes .../context/2017/context-2017-performance.tex | 65 + .../context/2017/context-2017-synctex.pdf | Bin 0 -> 58760 bytes .../context/2017/context-2017-synctex.tex | 102 ++ .../context/2017/context-2017-tables.pdf | Bin 0 -> 46060 bytes .../context/2017/context-2017-tables.tex | 175 ++ .../context/2019/context-2019-lmtx.pdf | Bin 0 -> 41345 bytes .../context/2019/context-2019-lmtx.tex | 201 +++ .../sources/general/magazines/mag-1104-mkiv.tex | 28 +- .../general/manuals/lowlevel/lowlevel-boxes.tex | 698 ++++++++ .../manuals/lowlevel/lowlevel-conditionals.tex | 1409 ++++++++++++++++ .../manuals/lowlevel/lowlevel-expansion.tex | 442 +++++ .../manuals/lowlevel/lowlevel-registers.tex | 251 +++ .../general/manuals/lowlevel/lowlevel-style.tex | 104 ++ .../manuals/luametafun/luametafun-arrow.tex | 166 ++ .../general/manuals/luametafun/luametafun-axis.tex | 83 + .../manuals/luametafun/luametafun-chart.tex | 441 +++++ .../manuals/luametafun/luametafun-contents.tex | 11 + .../manuals/luametafun/luametafun-contour.tex | 771 +++++++++ .../manuals/luametafun/luametafun-followtext.tex | 124 ++ .../manuals/luametafun/luametafun-function.tex | 296 ++++ .../general/manuals/luametafun/luametafun-grid.tex | 11 + .../manuals/luametafun/luametafun-interface.tex | 155 ++ .../manuals/luametafun/luametafun-introduction.tex | 88 + .../luametafun/luametafun-mesh-examples.tex | 161 ++ .../general/manuals/luametafun/luametafun-mesh.tex | 78 + .../manuals/luametafun/luametafun-outline.tex | 188 +++ .../manuals/luametafun/luametafun-placeholder.tex | 163 ++ .../manuals/luametafun/luametafun-shade.tex | 230 +++ .../manuals/luametafun/luametafun-style.tex | 55 + .../general/manuals/luametafun/luametafun-svg.tex | 75 + .../general/manuals/luametafun/luametafun-text.tex | 137 ++ .../manuals/luametafun/luametafun-titlepage.tex | 41 + .../general/manuals/luametafun/luametafun.tex | 31 + .../general/manuals/luametafun/mozilla-svg-001.svg | 1 + .../general/manuals/luametafun/mozilla-svg-002.svg | 10 + .../general/manuals/metafun/metafun-basics.tex | 2 +- metapost/context/base/mpiv/mp-blob.mpiv | 11 +- metapost/context/base/mpiv/mp-lmtx.mpxl | 157 +- metapost/context/base/mpiv/mp-luas.mpxl | 2 + metapost/context/base/mpiv/mp-mlib.mpiv | 20 +- metapost/context/base/mpiv/mp-tool.mpiv | 37 +- scripts/context/lua/mtx-install.lua | 41 +- scripts/context/lua/mtxrun.lua | 60 +- scripts/context/stubs/install/first-setup.sh | 23 +- scripts/context/stubs/mswin/mtxrun.lua | 60 +- scripts/context/stubs/unix/mtxrun | 60 +- scripts/context/stubs/win64/mtxrun.lua | 60 +- tex/context/base/mkii/cont-new.mkii | 2 +- tex/context/base/mkii/context.mkii | 2 +- tex/context/base/mkii/mult-en.mkii | 1 + tex/context/base/mkii/mult-it.mkii | 8 + tex/context/base/mkii/mult-ro.mkii | 1 + tex/context/base/mkiv/back-ini.lua | 23 + tex/context/base/mkiv/back-pdf.mkiv | 8 +- tex/context/base/mkiv/back-pdf.mkxl | 8 + tex/context/base/mkiv/cont-new.mkiv | 2 +- tex/context/base/mkiv/context.mkiv | 2 +- tex/context/base/mkiv/context.mkxl | 2 +- tex/context/base/mkiv/driv-shp.lua | 28 + tex/context/base/mkiv/font-con.lua | 7 +- tex/context/base/mkiv/font-lib.mkvi | 16 +- tex/context/base/mkiv/font-ocl.lua | 89 +- tex/context/base/mkiv/font-ogr.lua | 381 +++++ tex/context/base/mkiv/font-otl.lua | 1 + tex/context/base/mkiv/font-tpk.lua | 28 +- tex/context/base/mkiv/grph-chk.lua | 2 +- tex/context/base/mkiv/grph-img.lua | 50 +- tex/context/base/mkiv/lpdf-emb.lua | 397 +++-- tex/context/base/mkiv/lpdf-img.lua | 26 +- tex/context/base/mkiv/lpdf-ini.lua | 79 +- tex/context/base/mkiv/lpdf-lmt.lua | 111 +- tex/context/base/mkiv/lxml-css.lua | 17 +- tex/context/base/mkiv/meta-fig.mkiv | 7 +- tex/context/base/mkiv/meta-ini.mkiv | 3 + tex/context/base/mkiv/meta-ini.mkxl | 3 + tex/context/base/mkiv/mlib-cnt.lua | 1770 ++++++++++++++++++++ tex/context/base/mkiv/mlib-ctx.lua | 2 +- tex/context/base/mkiv/mlib-ctx.mkiv | 3 +- tex/context/base/mkiv/mlib-ctx.mkxl | 3 + tex/context/base/mkiv/mlib-lmp.lua | 8 + tex/context/base/mkiv/mlib-lua.lua | 14 +- tex/context/base/mkiv/mlib-pdf.lua | 13 +- tex/context/base/mkiv/mlib-pps.lua | 6 +- tex/context/base/mkiv/mlib-run.lua | 105 +- tex/context/base/mkiv/mlib-scn.lua | 19 + tex/context/base/mkiv/mlib-svg.lua | 1635 ++++++++++++++++++ tex/context/base/mkiv/mult-def.lua | 4 + tex/context/base/mkiv/mult-fun.lua | 1 + tex/context/base/mkiv/mult-mps.lua | 2 +- tex/context/base/mkiv/status-files.pdf | Bin 26674 -> 26690 bytes tex/context/base/mkiv/status-lua.pdf | Bin 268804 -> 267983 bytes tex/context/base/mkiv/strc-itm.mklx | 26 +- tex/context/base/mkiv/strc-itm.mkvi | 26 +- tex/context/base/mkiv/strc-ref.lua | 16 +- tex/context/base/mkiv/syst-aux.mkiv | 2 +- tex/context/base/mkiv/syst-aux.mkxl | 2 +- tex/context/base/mkiv/trac-deb.lua | 5 +- tex/context/base/mkiv/util-lua.lua | 10 +- tex/context/base/mkiv/util-sac.lua | 57 + tex/context/interface/mkii/keys-en.xml | 1 + tex/context/interface/mkii/keys-it.xml | 8 + tex/context/interface/mkii/keys-ro.xml | 1 + tex/context/interface/mkiv/context-en.xml | 2 + tex/context/interface/mkiv/i-context.pdf | Bin 893466 -> 893435 bytes tex/context/interface/mkiv/i-itemgroup.xml | 1 + tex/context/interface/mkiv/i-readme.pdf | Bin 61165 -> 61165 bytes tex/context/modules/mkiv/m-scite.mkiv | 7 +- tex/context/modules/mkiv/s-fonts-variable.mkiv | 34 +- tex/context/modules/mkiv/s-present-luatex.mkiv | 118 ++ tex/context/modules/mkiv/s-tugboat.mkiv | 818 ++++----- tex/generic/context/luatex/luatex-basics-nod.lua | 3 - tex/generic/context/luatex/luatex-fonts-merged.lua | 65 +- 156 files changed, 13190 insertions(+), 943 deletions(-) create mode 100644 doc/context/documents/general/manuals/lowlevel-boxes.pdf create mode 100644 doc/context/documents/general/manuals/lowlevel-conditionals.pdf create mode 100644 doc/context/documents/general/manuals/lowlevel-expansion.pdf create mode 100644 doc/context/documents/general/manuals/lowlevel-registers.pdf create mode 100644 doc/context/documents/general/manuals/luametafun.pdf create mode 100644 doc/context/presentations/context/2017/context-2017-css-selectors.pdf create mode 100644 doc/context/presentations/context/2017/context-2017-css-selectors.tex create mode 100644 doc/context/presentations/context/2017/context-2017-features-chaintest.pdf create mode 100644 doc/context/presentations/context/2017/context-2017-features-chaintest.tex create mode 100644 doc/context/presentations/context/2017/context-2017-features-kerntest.pdf create mode 100644 doc/context/presentations/context/2017/context-2017-features-kerntest.tex create mode 100644 doc/context/presentations/context/2017/context-2017-features-pairtest.pdf create mode 100644 doc/context/presentations/context/2017/context-2017-features-pairtest.tex create mode 100644 doc/context/presentations/context/2017/context-2017-features-singletest.pdf create mode 100644 doc/context/presentations/context/2017/context-2017-features-singletest.tex create mode 100644 doc/context/presentations/context/2017/context-2017-features-spacetest.pdf create mode 100644 doc/context/presentations/context/2017/context-2017-features-spacetest.tex create mode 100644 doc/context/presentations/context/2017/context-2017-features-substitutiontest.pdf create mode 100644 doc/context/presentations/context/2017/context-2017-features-substitutiontest.tex create mode 100644 doc/context/presentations/context/2017/context-2017-features.pdf create mode 100644 doc/context/presentations/context/2017/context-2017-features.tex create mode 100644 doc/context/presentations/context/2017/context-2017-performance.pdf create mode 100644 doc/context/presentations/context/2017/context-2017-performance.tex create mode 100644 doc/context/presentations/context/2017/context-2017-synctex.pdf create mode 100644 doc/context/presentations/context/2017/context-2017-synctex.tex create mode 100644 doc/context/presentations/context/2017/context-2017-tables.pdf create mode 100644 doc/context/presentations/context/2017/context-2017-tables.tex create mode 100644 doc/context/presentations/context/2019/context-2019-lmtx.pdf create mode 100644 doc/context/presentations/context/2019/context-2019-lmtx.tex create mode 100644 doc/context/sources/general/manuals/lowlevel/lowlevel-boxes.tex create mode 100644 doc/context/sources/general/manuals/lowlevel/lowlevel-conditionals.tex create mode 100644 doc/context/sources/general/manuals/lowlevel/lowlevel-expansion.tex create mode 100644 doc/context/sources/general/manuals/lowlevel/lowlevel-registers.tex create mode 100644 doc/context/sources/general/manuals/lowlevel/lowlevel-style.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-arrow.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-axis.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-chart.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-contents.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-contour.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-followtext.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-function.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-grid.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-interface.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-introduction.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-mesh-examples.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-mesh.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-outline.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-placeholder.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-shade.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-style.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-svg.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-text.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun-titlepage.tex create mode 100644 doc/context/sources/general/manuals/luametafun/luametafun.tex create mode 100644 doc/context/sources/general/manuals/luametafun/mozilla-svg-001.svg create mode 100644 doc/context/sources/general/manuals/luametafun/mozilla-svg-002.svg create mode 100644 tex/context/base/mkiv/font-ogr.lua create mode 100644 tex/context/base/mkiv/mlib-cnt.lua create mode 100644 tex/context/base/mkiv/mlib-svg.lua create mode 100644 tex/context/modules/mkiv/s-present-luatex.mkiv 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 Binary files a/doc/context/documents/general/magazines/mag-1104-mkiv.pdf and b/doc/context/documents/general/magazines/mag-1104-mkiv.pdf 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 Binary files /dev/null and b/doc/context/documents/general/manuals/lowlevel-boxes.pdf 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 Binary files /dev/null and b/doc/context/documents/general/manuals/lowlevel-conditionals.pdf 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 Binary files /dev/null and b/doc/context/documents/general/manuals/lowlevel-expansion.pdf 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 Binary files /dev/null and b/doc/context/documents/general/manuals/lowlevel-registers.pdf 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 Binary files /dev/null and b/doc/context/documents/general/manuals/luametafun.pdf 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 Binary files a/doc/context/documents/general/qrcs/setup-cs.pdf and b/doc/context/documents/general/qrcs/setup-cs.pdf 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 Binary files a/doc/context/documents/general/qrcs/setup-de.pdf and b/doc/context/documents/general/qrcs/setup-de.pdf 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 Binary files a/doc/context/documents/general/qrcs/setup-en.pdf and b/doc/context/documents/general/qrcs/setup-en.pdf 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 Binary files a/doc/context/documents/general/qrcs/setup-fr.pdf and b/doc/context/documents/general/qrcs/setup-fr.pdf 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 Binary files a/doc/context/documents/general/qrcs/setup-it.pdf and b/doc/context/documents/general/qrcs/setup-it.pdf 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 Binary files a/doc/context/documents/general/qrcs/setup-mapping-cs.pdf and b/doc/context/documents/general/qrcs/setup-mapping-cs.pdf 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 Binary files a/doc/context/documents/general/qrcs/setup-mapping-de.pdf and b/doc/context/documents/general/qrcs/setup-mapping-de.pdf 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 Binary files a/doc/context/documents/general/qrcs/setup-mapping-en.pdf and b/doc/context/documents/general/qrcs/setup-mapping-en.pdf 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 Binary files a/doc/context/documents/general/qrcs/setup-mapping-fr.pdf and b/doc/context/documents/general/qrcs/setup-mapping-fr.pdf 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 Binary files a/doc/context/documents/general/qrcs/setup-mapping-it.pdf and b/doc/context/documents/general/qrcs/setup-mapping-it.pdf 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 Binary files a/doc/context/documents/general/qrcs/setup-mapping-nl.pdf and b/doc/context/documents/general/qrcs/setup-mapping-nl.pdf 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 Binary files a/doc/context/documents/general/qrcs/setup-mapping-ro.pdf and b/doc/context/documents/general/qrcs/setup-mapping-ro.pdf 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 Binary files a/doc/context/documents/general/qrcs/setup-nl.pdf and b/doc/context/documents/general/qrcs/setup-nl.pdf 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 Binary files a/doc/context/documents/general/qrcs/setup-ro.pdf and b/doc/context/documents/general/qrcs/setup-ro.pdf 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 Binary files /dev/null and b/doc/context/presentations/context/2017/context-2017-css-selectors.pdf 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] + + + + b.one + b.two + b.one.two + b.three + b#first + c + d e + d e + d e e + d f + @foo = bar + @bar = foo + @bar = foo1 + @bar = foo2 + @bar = foo3 + @bar = foo+4 + g + g gg d + g gg f + g gg f.one + g + g gg f.two + g gg f.three + g f.one + g f.three + @whatever = four five six + +\stopbuffer + +\startbuffer[selector-002] + + + + title 1 + title 2 + title 3 + title 4 + +\stopbuffer + +\startbuffer[selector-003] + + + + title 1 + title 1.1 + title 2 + title 2.1 + title 3 + title 3.1 + title 4 + title 4.1 + +\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 Binary files /dev/null and b/doc/context/presentations/context/2017/context-2017-features-chaintest.pdf 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 Binary files /dev/null and b/doc/context/presentations/context/2017/context-2017-features-kerntest.pdf 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 Binary files /dev/null and b/doc/context/presentations/context/2017/context-2017-features-pairtest.pdf 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 Binary files /dev/null and b/doc/context/presentations/context/2017/context-2017-features-singletest.pdf 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 Binary files /dev/null and b/doc/context/presentations/context/2017/context-2017-features-spacetest.pdf 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 Binary files /dev/null and b/doc/context/presentations/context/2017/context-2017-features-substitutiontest.pdf 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 Binary files /dev/null and b/doc/context/presentations/context/2017/context-2017-features.pdf 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 Binary files /dev/null and b/doc/context/presentations/context/2017/context-2017-performance.pdf 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 Binary files /dev/null and b/doc/context/presentations/context/2017/context-2017-synctex.pdf 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 Binary files /dev/null and b/doc/context/presentations/context/2017/context-2017-tables.pdf 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 Binary files /dev/null and b/doc/context/presentations/context/2019/context-2019-lmtx.pdf 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 +\stoptyping + +or in \TEX\ speak: + +\starttyping +" +\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 + 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 @@ + 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 @@ + + + + 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 >> BDC %s EMC ET"] - local f_actual_text_two = formatters["BT /Span << /ActualText >> BDC %s EMC ET"] - local f_actual_text_one_b = formatters["BT /Span << /ActualText >> BDC"] - local f_actual_text_two_b = formatters["BT /Span << /ActualText >> BDC"] + local f_actual_text_p = formatters["BT /Span << /ActualText >> BDC %s EMC ET"] local f_actual_text_b = formatters["BT /Span << /ActualText >> BDC"] local s_actual_text_e = "EMC ET" local f_actual_text_b_not = formatters["/Span << /ActualText >> BDC"] - local f_actual_text_one_b_not = formatters["/Span << /ActualText >> BDC"] - local f_actual_text_two_b_not = formatters["/Span << /ActualText >> BDC"] local s_actual_text_e_not = "EMC" local f_actual_text = formatters["/Span <> 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 = 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 +-- glyph +-- 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 Binary files a/tex/context/base/mkiv/status-files.pdf and b/tex/context/base/mkiv/status-files.pdf differ diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf index 726657bc3..8008a61ee 100644 Binary files a/tex/context/base/mkiv/status-lua.pdf and b/tex/context/base/mkiv/status-lua.pdf 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 @@ + 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 @@ + @@ -873,6 +874,7 @@ + @@ -973,9 +975,12 @@ + + + @@ -1293,6 +1298,7 @@ + @@ -1303,6 +1309,7 @@ + @@ -1310,6 +1317,7 @@ + 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 @@ + 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 @@ + @@ -18310,6 +18311,7 @@ + diff --git a/tex/context/interface/mkiv/i-context.pdf b/tex/context/interface/mkiv/i-context.pdf index a6e83085f..5c0fba19a 100644 Binary files a/tex/context/interface/mkiv/i-context.pdf and b/tex/context/interface/mkiv/i-context.pdf 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 @@ + diff --git a/tex/context/interface/mkiv/i-readme.pdf b/tex/context/interface/mkiv/i-readme.pdf index 4d002f1b0..db5adf50d 100644 Binary files a/tex/context/interface/mkiv/i-readme.pdf and b/tex/context/interface/mkiv/i-readme.pdf 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, -- cgit v1.2.3