summaryrefslogtreecommitdiff
path: root/tex
diff options
context:
space:
mode:
Diffstat (limited to 'tex')
-rw-r--r--tex/context/base/mkii/cont-new.mkii2
-rw-r--r--tex/context/base/mkii/context.mkii2
-rw-r--r--tex/context/base/mkiv/cont-new.mkiv2
-rw-r--r--tex/context/base/mkiv/context.mkiv2
-rw-r--r--tex/context/base/mkiv/data-tex.lua3
-rw-r--r--tex/context/base/mkiv/font-ctx.lua4
-rw-r--r--tex/context/base/mkiv/mult-fun.lua10
-rw-r--r--tex/context/base/mkiv/mult-low.lua6
-rw-r--r--tex/context/base/mkiv/mult-mps.lua7
-rw-r--r--tex/context/base/mkiv/status-files.pdfbin26060 -> 26103 bytes
-rw-r--r--tex/context/base/mkiv/status-lua.pdfbin253964 -> 253364 bytes
-rw-r--r--tex/context/base/mkiv/strc-pag.lua6
-rw-r--r--tex/context/base/mkxl/catc-ctx.mkxl162
-rw-r--r--tex/context/base/mkxl/catc-def.mkxl160
-rw-r--r--tex/context/base/mkxl/catc-ini.mkxl37
-rw-r--r--tex/context/base/mkxl/char-ini.mkxl22
-rw-r--r--tex/context/base/mkxl/char-tex.lmt763
-rw-r--r--tex/context/base/mkxl/cont-new.mkxl2
-rw-r--r--tex/context/base/mkxl/context.mkxl3
-rw-r--r--tex/context/base/mkxl/font-fbk.lmt36
-rw-r--r--tex/context/base/mkxl/font-ini.lmt40
-rw-r--r--tex/context/base/mkxl/font-lib.mklx6
-rw-r--r--tex/context/base/mkxl/font-vfc.lmt83
-rw-r--r--tex/context/base/mkxl/font-vir.lmt159
-rw-r--r--tex/context/base/mkxl/grph-inc.lmt2160
-rw-r--r--tex/context/base/mkxl/grph-inc.mkxl4
-rw-r--r--tex/context/base/mkxl/grph-trf.lmt126
-rw-r--r--tex/context/base/mkxl/grph-trf.mkxl148
-rw-r--r--tex/context/base/mkxl/lpdf-col.lmt7
-rw-r--r--tex/context/base/mkxl/lpdf-lmt.lmt7
-rw-r--r--tex/context/base/mkxl/lxml-ini.mkxl140
-rw-r--r--tex/context/base/mkxl/meta-fnt.lmt259
-rw-r--r--tex/context/base/mkxl/meta-fnt.mkxl30
-rw-r--r--tex/context/base/mkxl/mlib-fio.lmt41
-rw-r--r--tex/context/base/mkxl/mlib-int.lmt18
-rw-r--r--tex/context/base/mkxl/mlib-lmt.lmt31
-rw-r--r--tex/context/base/mkxl/mlib-pdf.lmt131
-rw-r--r--tex/context/base/mkxl/mlib-run.lmt30
-rw-r--r--tex/context/base/mkxl/page-lay.mkxl7
-rw-r--r--tex/context/base/mkxl/syst-lua.lmt107
-rw-r--r--tex/context/base/mkxl/trac-inf.lmt5
-rw-r--r--tex/context/modules/mkiv/m-tikz.mkiv5
-rw-r--r--tex/context/modules/mkiv/s-system-macros.mkxl122
-rw-r--r--tex/generic/context/luatex/luatex-fonts-merged.lua2
44 files changed, 4049 insertions, 848 deletions
diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii
index 79a8f46d6..01555af45 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{2020.12.10 22:23}
+\newcontextversion{2020.12.15 10:10}
%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 80a42b727..8b46e9081 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{2020.12.10 22:23}
+\edef\contextversion{2020.12.15 10:10}
%D For those who want to use this:
diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv
index 5b7f4831b..beded1600 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{2020.12.10 22:23}
+\newcontextversion{2020.12.15 10:10}
%D This file is loaded at runtime, thereby providing an excellent place for hacks,
%D patches, extensions and new features. There can be local overloads in cont-loc
diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv
index 8b645e33a..4aaaf492d 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{2020.12.10 22:23}
+\edef\contextversion{2020.12.15 10:10}
%D Kind of special:
diff --git a/tex/context/base/mkiv/data-tex.lua b/tex/context/base/mkiv/data-tex.lua
index 2e8642cdd..55383c1fe 100644
--- a/tex/context/base/mkiv/data-tex.lua
+++ b/tex/context/base/mkiv/data-tex.lua
@@ -149,6 +149,9 @@ local function textopener(tag,filename,filehandle,coding)
filename = filename,
noflines = noflines,
-- currentline = 0,
+ endoffile = function()
+ return not lines or currentline >= noflines
+ end,
close = function()
local usedname = popinputname() -- should match filename
if trace_locating then
diff --git a/tex/context/base/mkiv/font-ctx.lua b/tex/context/base/mkiv/font-ctx.lua
index 5c93f302f..eead8abff 100644
--- a/tex/context/base/mkiv/font-ctx.lua
+++ b/tex/context/base/mkiv/font-ctx.lua
@@ -1840,10 +1840,6 @@ function mappings.reset()
lpdf.setmapfile("") -- tricky ... backend related
end
-function mappings.getentry(...)
- return lpdf.getmapentry(...) -- tricky ... backend related
-end
-
implement {
name = "loadmapfile",
actions = mappings.loadfile,
diff --git a/tex/context/base/mkiv/mult-fun.lua b/tex/context/base/mkiv/mult-fun.lua
index 095900861..41ca68613 100644
--- a/tex/context/base/mkiv/mult-fun.lua
+++ b/tex/context/base/mkiv/mult-fun.lua
@@ -2,7 +2,7 @@ return {
internals = {
--
"nocolormodel", "greycolormodel", "graycolormodel", "rgbcolormodel", "cmykcolormodel",
- "shadefactor",
+ "shadefactor", "shadeoffset",
"textextoffset", "textextanchor",
"normaltransparent", "multiplytransparent", "screentransparent", "overlaytransparent",
"softlighttransparent", "hardlighttransparent", "colordodgetransparent", "colorburntransparent",
@@ -17,7 +17,7 @@ return {
"drawoptionsfactor",
"dq", "sq",
"crossingscale", "crossingoption",
- "contextlmtxmode",
+ "contextlmtxmode", "metafunversion",
--
-- for the moment we put these here as they need to stand out
--
@@ -74,6 +74,7 @@ return {
"withshadedomain", "withshademethod", "withshadefactor", "withshadevector",
"withshadecenter", "withshadedirection", "withshaderadius", "withshadetransform",
"withshadestep", "withshadefraction", "withshadeorigin", "shownshadevector", "shownshadeorigin",
+ "shownshadedirection", "shownshadecenter",
"cmyk", "spotcolor", "multitonecolor", "namedcolor",
"drawfill", "undrawfill",
"inverted", "uncolored", "softened", "grayed", "greyed",
@@ -85,6 +86,7 @@ return {
"withmask", "bitmapimage",
"colordecimals", "ddecimal", "dddecimal", "ddddecimal", "colordecimalslist",
"textext", "thetextext", "rawtextext", "textextoffset", "texbox", "thetexbox", "rawtexbox", "istextext",
+ "rawmadetext", "validtexbox", "onetimetextext", "rawfmttext", "thefmttext", "fmttext", "onetimefmttext",
"notcached", "keepcached",
"verbatim",
"thelabel", "label",
@@ -191,10 +193,10 @@ return {
--
"isarray", "prefix", "isobject",
--
- "comment", "report", "lua", "mp", "MP", "luacall",
+ "comment", "report", "lua", "lualist", "mp", "MP", "luacall",
--
"mirrored", "mirroredabout",
--
- "scriptindex",
+ "scriptindex", "newscriptindex",
},
}
diff --git a/tex/context/base/mkiv/mult-low.lua b/tex/context/base/mkiv/mult-low.lua
index be507394d..fb786231e 100644
--- a/tex/context/base/mkiv/mult-low.lua
+++ b/tex/context/base/mkiv/mult-low.lua
@@ -50,7 +50,7 @@ return {
"inicatcodes",
"ctxcatcodes", "texcatcodes", "notcatcodes", "txtcatcodes", "vrbcatcodes",
"prtcatcodes", "nilcatcodes", "luacatcodes", "tpacatcodes", "tpbcatcodes",
- "xmlcatcodes", "ctdcatcodes",
+ "xmlcatcodes", "ctdcatcodes", "rlncatcodes",
--
"escapecatcode", "begingroupcatcode", "endgroupcatcode", "mathshiftcatcode", "alignmentcatcode",
"endoflinecatcode", "parametercatcode", "superscriptcatcode", "subscriptcatcode", "ignorecatcode",
@@ -362,8 +362,8 @@ return {
"getvalue", "getuvalue", "setvalue", "setevalue", "setgvalue", "setxvalue", "letvalue", "letgvalue",
"resetvalue", "undefinevalue", "ignorevalue",
"setuvalue", "setuevalue", "setugvalue", "setuxvalue",
- --
- "globallet", "glet", "udef", "ugdef", "uedef", "uxdef", "checked", "unique",
+ -- glet
+ "globallet", "udef", "ugdef", "uedef", "uxdef", "checked", "unique",
--
"getparameters", "geteparameters", "getgparameters", "getxparameters", "forgetparameters", "copyparameters",
--
diff --git a/tex/context/base/mkiv/mult-mps.lua b/tex/context/base/mkiv/mult-mps.lua
index d2bad606c..d994a3f65 100644
--- a/tex/context/base/mkiv/mult-mps.lua
+++ b/tex/context/base/mkiv/mult-mps.lua
@@ -27,7 +27,6 @@ return {
"shipout", "show", "showdependencies", "showtoken", "showvariable",
"special",
"begingroup", "endgroup", "of", "curl", "tension", "and", "controls",
- "interpath", "on", "off",
"def", "vardef", "enddef", "expr", "suffix", "text", "primary", "secondary",
"tertiary", "primarydef", "secondarydef", "tertiarydef",
"randomseed", "also", "contour", "doublepath",
@@ -68,11 +67,14 @@ return {
"fontpart", "fontsize", "glyph", "restoreclipcolor", "troffmode",
--
"runscript", "maketext", "numbersystem",
+ "overloadmode", "setproperty",
},
commands = {
+ "on", "off",
+ "interpath",
"upto", "downto",
"beginfig", "endfig",
- "beginglyph", "endglyph", -- actually a mult-fun one
+ "beginglyph", "endglyph", "beginfont", "endfont", -- actually a mult-fun one
"rotatedaround", "reflectedabout",
"arrowhead",
"currentpen", "currentpicture", "cuttings",
@@ -119,6 +121,7 @@ return {
--
"triplet", "quadruplet", "totransform", "bymatrix",
--
+ "primitive", "permanent", "immutable", "mutable", "frozen",
},
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 709b9330d..e4baa5bde 100644
--- a/tex/context/base/mkiv/status-files.pdf
+++ b/tex/context/base/mkiv/status-files.pdf
Binary files differ
diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf
index 37f1aff8a..03b142f98 100644
--- a/tex/context/base/mkiv/status-lua.pdf
+++ b/tex/context/base/mkiv/status-lua.pdf
Binary files differ
diff --git a/tex/context/base/mkiv/strc-pag.lua b/tex/context/base/mkiv/strc-pag.lua
index 47fa9463c..6f9cdd429 100644
--- a/tex/context/base/mkiv/strc-pag.lua
+++ b/tex/context/base/mkiv/strc-pag.lua
@@ -35,6 +35,8 @@ local stopapplyprocessor = processors.stopapply
local texsetcount = tex.setcount
local texgetcount = tex.getcount
+local texconditionals = tex.conditionals
+
local ctx_convertnumber = context.convertnumber
-- storage
@@ -319,6 +321,10 @@ function pages.on_right(n)
end
end
+function pages.has_changed()
+ return texconditionals.layouthaschanged
+end
+
function pages.in_body(n)
return texgetcount("pagebodymode") > 0
end
diff --git a/tex/context/base/mkxl/catc-ctx.mkxl b/tex/context/base/mkxl/catc-ctx.mkxl
deleted file mode 100644
index fcaec65d0..000000000
--- a/tex/context/base/mkxl/catc-ctx.mkxl
+++ /dev/null
@@ -1,162 +0,0 @@
-%D \module
-%D [ file=catc-cys,
-%D version=2006.09.18,
-%D title=\CONTEXT\ Catcode Macros,
-%D subtitle=Extra Tables,
-%D author=Hans Hagen,
-%D date=\currentdate,
-%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
-%C
-%C This module is part of the \CONTEXT\ macro||package and is
-%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
-%C details.
-
-%D We prefer to define relevant catcode tables in this file instead
-%D of everywhere around.
-
-\ifdefined \ctxcatcodes \else \newcatcodetable \ctxcatcodes \fi
-\ifdefined \prtcatcodes \else \newcatcodetable \prtcatcodes \fi
-\ifdefined \txtcatcodes \else \newcatcodetable \txtcatcodes \fi
-%ifdefined \mthcatcodes \else \newcatcodetable \mthcatcodes \fi % math, not used, too tricky
-\ifdefined \tpacatcodes \else \newcatcodetable \tpacatcodes \fi % { }
-\ifdefined \tpbcatcodes \else \newcatcodetable \tpbcatcodes \fi % < >
-
-\setnewconstant \doublecommentsignal "10FF25 %% < 110000 (last valid range)
-
-\startcatcodetable \ctxcatcodes
- \catcode\tabasciicode \spacecatcode
- \catcode\endoflineasciicode \endoflinecatcode
- \catcode\formfeedasciicode \endoflinecatcode
- \catcode\spaceasciicode \spacecatcode
- \catcode\endoffileasciicode \ignorecatcode
- % \catcode\circumflexasciicode\superscriptcatcode
- % \catcode\underscoreasciicode\subscriptcatcode
- % \catcode\ampersandasciicode \alignmentcatcode
- \catcode\underscoreasciicode\othercatcode
- \catcode\circumflexasciicode\othercatcode
- \catcode\ampersandasciicode \othercatcode
- \catcode\backslashasciicode \escapecatcode
- \catcode\leftbraceasciicode \begingroupcatcode
- \catcode\rightbraceasciicode\endgroupcatcode
- \catcode\dollarasciicode \mathshiftcatcode
- \catcode\hashasciicode \parametercatcode
- \catcode\commentasciicode \commentcatcode
- \catcode\tildeasciicode \activecatcode
- \catcode\barasciicode \activecatcode
-\stopcatcodetable
-
-\startcatcodetable \prtcatcodes
- \catcode\tabasciicode \spacecatcode
- \catcode\endoflineasciicode \endoflinecatcode
- \catcode\formfeedasciicode \endoflinecatcode
- \catcode\spaceasciicode \spacecatcode
- \catcode\endoffileasciicode \ignorecatcode
- \catcode\circumflexasciicode \superscriptcatcode % candidate
- \catcode\underscoreasciicode \lettercatcode
- \catcode\ampersandasciicode \alignmentcatcode
- \catcode\backslashasciicode \escapecatcode
- \catcode\leftbraceasciicode \begingroupcatcode
- \catcode\rightbraceasciicode \endgroupcatcode
- \catcode\dollarasciicode \mathshiftcatcode
- \catcode\hashasciicode \parametercatcode
- \catcode\commentasciicode \commentcatcode
- \catcode\atsignasciicode \lettercatcode
- \catcode\exclamationmarkasciicode\lettercatcode
- \catcode\questionmarkasciicode \lettercatcode
- \catcode\tildeasciicode \activecatcode
- \catcode\barasciicode \activecatcode
-\stopcatcodetable
-
-\startcatcodetable \tpacatcodes
- \catcode\tabasciicode \othercatcode
- \catcode\endoflineasciicode \othercatcode
- \catcode\formfeedasciicode \othercatcode
- \catcode\spaceasciicode \othercatcode
- \catcode\endoffileasciicode \othercatcode
- \catcode\leftbraceasciicode \begingroupcatcode
- \catcode\rightbraceasciicode\endgroupcatcode
-\stopcatcodetable
-
-\startcatcodetable \tpbcatcodes
- \catcode\tabasciicode \othercatcode
- \catcode\endoflineasciicode \othercatcode
- \catcode\formfeedasciicode \othercatcode
- \catcode\spaceasciicode \othercatcode
- \catcode\endoffileasciicode \othercatcode
- \catcode\lessthanasciicode \begingroupcatcode
- \catcode\morethanasciicode \endgroupcatcode
-\stopcatcodetable
-
-\startcatcodetable \txtcatcodes
- \catcode\tabasciicode \spacecatcode
- \catcode\endoflineasciicode \endoflinecatcode
- \catcode\formfeedasciicode \endoflinecatcode
- \catcode\spaceasciicode \spacecatcode
- \catcode\endoffileasciicode \ignorecatcode
- \catcode\backslashasciicode \escapecatcode
- \catcode\leftbraceasciicode \begingroupcatcode
- \catcode\rightbraceasciicode\endgroupcatcode
- \catcode\doublecommentsignal\commentcatcode
-\stopcatcodetable
-
-\letcatcodecommand \ctxcatcodes \barasciicode \relax
-\letcatcodecommand \ctxcatcodes \tildeasciicode \relax
-
-\pushoverloadmode
-
- \catcodetable \ctxcatcodes
-\let \defaultcatcodetable \ctxcatcodes
-
-\popoverloadmode
-
-% for the moment here:
-
-\permanent\protected\def\starttexcode
- {\pushcatcodetable
- \catcodetable\prtcatcodes}
-
-\permanent\protected\def\stoptexcode
- {\popcatcodetable}
-
-\permanent\protected\def\startcontextcode
- {\pushcatcodetable
- \catcodetable\ctxcatcodes}
-
-\permanent\protected\def\stopcontextcode
- {\popcatcodetable}
-
-% not visible, only for special cases
-
-\newcatcodetable \ctdcatcodes % context definitions
-
-\startcatcodetable \ctdcatcodes
- \catcode\tabasciicode \ignorecatcode
- \catcode\endoflineasciicode \ignorecatcode
- \catcode\formfeedasciicode \ignorecatcode
- \catcode\spaceasciicode \ignorecatcode
- \catcode\endoffileasciicode \ignorecatcode
- \catcode\circumflexasciicode \superscriptcatcode % candidate
- \catcode\underscoreasciicode \lettercatcode
- \catcode\ampersandasciicode \alignmentcatcode
-% \catcode\colonasciicode \lettercatcode % candidate
- \catcode\backslashasciicode \escapecatcode
- \catcode\leftbraceasciicode \begingroupcatcode
- \catcode\rightbraceasciicode \endgroupcatcode
- \catcode\dollarasciicode \mathshiftcatcode
- \catcode\hashasciicode \parametercatcode
- \catcode\commentasciicode \commentcatcode
- \catcode\atsignasciicode \lettercatcode
- \catcode\exclamationmarkasciicode\lettercatcode
- \catcode\questionmarkasciicode \lettercatcode
- \catcode\tildeasciicode \activecatcode
- \catcode\barasciicode \activecatcode
-\stopcatcodetable
-
-\permanent\protected\def\startcontextdefinitioncode
- {\pushcatcodetable
- \catcodetable\ctdcatcodes}
-
-\permanent\protected\def\stopcontextdefinitioncode
- {\popcatcodetable}
-
-\endinput
diff --git a/tex/context/base/mkxl/catc-def.mkxl b/tex/context/base/mkxl/catc-def.mkxl
index e93345e1c..a67d94e44 100644
--- a/tex/context/base/mkxl/catc-def.mkxl
+++ b/tex/context/base/mkxl/catc-def.mkxl
@@ -11,14 +11,24 @@
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.
-%D The following catcode tables are rather \CONTEXT\ independent.
+%D Contrary to \MKIV\ we define the catcode tables in one file. The original split
+%D was a prelude to a split approach where context would run on top of a basic layer
+%D (so that we could make smaller alternative formats) but that idea (from the early
+%D days of \LUATEX) was dropped when I realized that there is not common ground to
+%D cover between formats. It is simply not worth the trouble.
-\ifdefined\nilcatcodes \else \newcatcodetable \nilcatcodes \fi
-\ifdefined\texcatcodes \else \newcatcodetable \texcatcodes \fi
-\ifdefined\luacatcodes \else \newcatcodetable \luacatcodes \fi
-\ifdefined\notcatcodes \else \newcatcodetable \notcatcodes \fi
-\ifdefined\vrbcatcodes \else \newcatcodetable \vrbcatcodes \fi
-\ifdefined\prtcatcodes \else \newcatcodetable \prtcatcodes \fi
+\ifdefined\nilcatcodes \else \newcatcodetable \nilcatcodes \fi
+\ifdefined\texcatcodes \else \newcatcodetable \texcatcodes \fi
+\ifdefined\luacatcodes \else \newcatcodetable \luacatcodes \fi
+\ifdefined\notcatcodes \else \newcatcodetable \notcatcodes \fi
+\ifdefined\rlncatcodes \else \newcatcodetable \rlncatcodes \fi
+\ifdefined\vrbcatcodes \else \newcatcodetable \vrbcatcodes \fi
+\ifdefined\prtcatcodes \else \newcatcodetable \prtcatcodes \fi
+\ifdefined\ctxcatcodes \else \newcatcodetable \ctxcatcodes \fi
+\ifdefined\txtcatcodes \else \newcatcodetable \txtcatcodes \fi
+\ifdefined\tpacatcodes \else \newcatcodetable \tpacatcodes \fi % { }
+\ifdefined\tpbcatcodes \else \newcatcodetable \tpbcatcodes \fi % < >
+\ifdefined\ctdcatcodes \else \newcatcodetable \ctdcatcodes \fi % context definitions
\startcatcodetable \nilcatcodes
\catcode\tabasciicode \spacecatcode
@@ -88,6 +98,30 @@
\catcode\barasciicode \othercatcode
\stopcatcodetable
+\startcatcodetable \rlncatcodes
+ \catcode\tabasciicode \othercatcode
+ \catcode\endoflineasciicode \othercatcode
+ \catcode\formfeedasciicode \othercatcode
+ \catcode\spaceasciicode \spacecatcode % maybe we also need a vector with space being other
+ \catcode\endoffileasciicode \othercatcode
+ \catcode\circumflexasciicode \othercatcode
+ \catcode\underscoreasciicode \othercatcode
+ \catcode\ampersandasciicode \othercatcode
+ \catcode\tildeasciicode \othercatcode
+ \catcode\hashasciicode \othercatcode
+ \catcode\dollarasciicode \othercatcode
+ \catcode\commentasciicode \othercatcode
+ \catcode\lessthanasciicode \othercatcode
+ \catcode\morethanasciicode \othercatcode
+ \catcode\leftbraceasciicode \othercatcode
+ \catcode\rightbraceasciicode \othercatcode
+ \catcode\doublequoteasciicode \othercatcode
+ \catcode\singlequoteasciicode \othercatcode
+ \catcode\forwardslashasciicode\othercatcode
+ \catcode\backslashasciicode \othercatcode
+ \catcode\barasciicode \othercatcode
+\stopcatcodetable
+
\startcatcodetable \vrbcatcodes % probably less needed
\catcode\tabasciicode \othercatcode
\catcode\endoflineasciicode\othercatcode
@@ -119,6 +153,104 @@
\catcode\barasciicode \activecatcode
\stopcatcodetable
+\startcatcodetable \ctxcatcodes
+ \catcode\tabasciicode \spacecatcode
+ \catcode\endoflineasciicode \endoflinecatcode
+ \catcode\formfeedasciicode \endoflinecatcode
+ \catcode\spaceasciicode \spacecatcode
+ \catcode\endoffileasciicode \ignorecatcode
+ % \catcode\circumflexasciicode\superscriptcatcode
+ % \catcode\underscoreasciicode\subscriptcatcode
+ % \catcode\ampersandasciicode \alignmentcatcode
+ \catcode\underscoreasciicode\othercatcode
+ \catcode\circumflexasciicode\othercatcode
+ \catcode\ampersandasciicode \othercatcode
+ \catcode\backslashasciicode \escapecatcode
+ \catcode\leftbraceasciicode \begingroupcatcode
+ \catcode\rightbraceasciicode\endgroupcatcode
+ \catcode\dollarasciicode \mathshiftcatcode
+ \catcode\hashasciicode \parametercatcode
+ \catcode\commentasciicode \commentcatcode
+ \catcode\tildeasciicode \activecatcode
+ \catcode\barasciicode \activecatcode
+\stopcatcodetable
+
+\startcatcodetable \tpacatcodes
+ \catcode\tabasciicode \othercatcode
+ \catcode\endoflineasciicode \othercatcode
+ \catcode\formfeedasciicode \othercatcode
+ \catcode\spaceasciicode \othercatcode
+ \catcode\endoffileasciicode \othercatcode
+ \catcode\leftbraceasciicode \begingroupcatcode
+ \catcode\rightbraceasciicode\endgroupcatcode
+\stopcatcodetable
+
+\startcatcodetable \tpbcatcodes
+ \catcode\tabasciicode \othercatcode
+ \catcode\endoflineasciicode \othercatcode
+ \catcode\formfeedasciicode \othercatcode
+ \catcode\spaceasciicode \othercatcode
+ \catcode\endoffileasciicode \othercatcode
+ \catcode\lessthanasciicode \begingroupcatcode
+ \catcode\morethanasciicode \endgroupcatcode
+\stopcatcodetable
+
+\setnewconstant \doublecommentsignal "10FF25 %% < 110000 (last valid range)
+
+\startcatcodetable \txtcatcodes
+ \catcode\tabasciicode \spacecatcode
+ \catcode\endoflineasciicode \endoflinecatcode
+ \catcode\formfeedasciicode \endoflinecatcode
+ \catcode\spaceasciicode \spacecatcode
+ \catcode\endoffileasciicode \ignorecatcode
+ \catcode\backslashasciicode \escapecatcode
+ \catcode\leftbraceasciicode \begingroupcatcode
+ \catcode\rightbraceasciicode\endgroupcatcode
+ \catcode\doublecommentsignal\commentcatcode
+\stopcatcodetable
+
+\startcatcodetable \ctdcatcodes
+ \catcode\tabasciicode \ignorecatcode
+ \catcode\endoflineasciicode \ignorecatcode
+ \catcode\formfeedasciicode \ignorecatcode
+ \catcode\spaceasciicode \ignorecatcode
+ \catcode\endoffileasciicode \ignorecatcode
+ \catcode\circumflexasciicode \superscriptcatcode % candidate
+ \catcode\underscoreasciicode \lettercatcode
+ \catcode\ampersandasciicode \alignmentcatcode
+ % \catcode\colonasciicode \lettercatcode % candidate
+ \catcode\backslashasciicode \escapecatcode
+ \catcode\leftbraceasciicode \begingroupcatcode
+ \catcode\rightbraceasciicode \endgroupcatcode
+ \catcode\dollarasciicode \mathshiftcatcode
+ \catcode\hashasciicode \parametercatcode
+ \catcode\commentasciicode \commentcatcode
+ \catcode\atsignasciicode \lettercatcode
+ \catcode\exclamationmarkasciicode\lettercatcode
+ \catcode\questionmarkasciicode \lettercatcode
+ \catcode\tildeasciicode \activecatcode
+ \catcode\barasciicode \activecatcode
+\stopcatcodetable
+
+%D From now on we can use the protection mechanisms.
+
+\permanent\protected\def\unprotect {\pushcatcodetable\catcodetable\prtcatcodes}
+\permanent\protected\def\protect {\popcatcodetable}
+
+\permanent\protected\def\starttexcode {\pushcatcodetable\catcodetable\prtcatcodes}
+\permanent\protected\def\stoptexcode {\popcatcodetable}
+
+\permanent\protected\def\startcontextcode {\pushcatcodetable\catcodetable\ctxcatcodes}
+\permanent\protected\def\stopcontextcode {\popcatcodetable}
+
+\permanent\protected\def\startcontextdefinitioncode {\pushcatcodetable\catcodetable\ctdcatcodes}
+\permanent\protected\def\stopcontextdefinitioncode {\popcatcodetable}
+
+%D We have a few active characters (left):
+
+\letcatcodecommand \ctxcatcodes \barasciicode \relax
+\letcatcodecommand \ctxcatcodes \tildeasciicode \relax
+
%D Because some characters have a special meaning, we provide shortcuts to their
%D character representation. Some will be overloaded (which might change).
@@ -133,15 +265,9 @@
\chardef \\ = \backslashasciicode
\chardef \| = \barasciicode
-% way too wide in lm, so one can also use:
-%
-% \def\_{\dontleavehmode \kern.06em \vbox{\hrule width.3em}} % this will become a \chardef
-
-%D From now on we can use the protection mechanisms.
-
-\permanent\protected\def\unprotect{\pushcatcodetable\setcatcodetable\prtcatcodes}
-\permanent\protected\def\protect {\popcatcodetable}
-
-% \prependtoks \catcodetable\ctxcatcodes \to \everyjob
+\pushoverloadmode
+ \catcodetable \ctxcatcodes
+ \let \defaultcatcodetable \ctxcatcodes
+\popoverloadmode
\endinput
diff --git a/tex/context/base/mkxl/catc-ini.mkxl b/tex/context/base/mkxl/catc-ini.mkxl
index b7844fe89..7d23c37d8 100644
--- a/tex/context/base/mkxl/catc-ini.mkxl
+++ b/tex/context/base/mkxl/catc-ini.mkxl
@@ -11,35 +11,14 @@
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.
-%D We've split the functionality of syst-cat.* over more files now so that we can
-%D load more selectively.
-
\registerctxluafile{catc-ini}{}
\unprotect
%D A long standing wish has been the availability of catcode arrays. Because
-%D traditional \TEX\ does not provide this we implement a fake method in the
-%D \MKII\ file. There is some overlap in code with \MKII\ but we take that
-%D for granted. Also, in \MKIV\ less active characters are used.
-
-% \begingroup
-%
-% \catcode\tabasciicode \activecatcode
-% \catcode\formfeedasciicode \activecatcode
-% \catcode\endoflineasciicode\activecatcode
-%
-% \letcharcode\tabasciicode \relax
-% \letcharcode\newlineasciicode \relax
-% \letcharcode\formfeedasciicode \relax
-% \letcharcode\endoflineasciicode\relax
-%
-% \xdef\activetabtoken {\Uchar\tabasciicode } % \gdef\activetabtoken {^^I}
-% \xdef\outputnewlinechar {\Uchar\newlineasciicode } % \gdef\outputnewlinechar {^^J}
-% \xdef\activeformfeedtoken {\Uchar\formfeedasciicode } % \gdef\activeformfeedtoken {^^L}
-% \xdef\activeendoflinetoken{\Uchar\endoflineasciicode} % \gdef\activeendoflinetoken{^^M}
-%
-% \endgroup
+%D traditional \TEX\ does not provide this we implement a fake method in the \MKII\
+%D file. There is some overlap in code with \MKII\ but we take that for granted.
+%D Also, in \MKIV\ less active characters are used.
\begingroup
\letcharcode\newlineasciicode\relax \xdef\outputnewlinechar{\Uchar\newlineasciicode}
@@ -58,7 +37,7 @@
\endgroup}
%D We predefine some prefixes ahead of syst-aux and mult-sys. We reserve 8 slots for
-%D catcodes.
+%D catcodes. (This active mess probably needs an update some day.)
\installsystemnamespace {catcodelet} % let : \let
\installsystemnamespace {catcodedef} % def : \def
@@ -225,13 +204,13 @@
\permanent\protected\def\pushcatcodetable
{\advance\c_syst_catcodes_level\plusone
\syst_catcodes_trace_push
- \expandafter\chardef\csname\??catcodetablet\number\c_syst_catcodes_level\endcsname\currentcatcodetable}
+ \expandafter\integerdef\csname\??catcodetablet\number\c_syst_catcodes_level\endcsname\currentcatcodetable}
\permanent\protected\def\popcatcodetable
{\ifcase\c_syst_catcodes_level
\syst_catcodes_trace_nesting_error
\else
- \expandafter\catcodetable\csname\??catcodetablet\number\c_syst_catcodes_level\endcsname
+ \catcodetable\csname\??catcodetablet\number\c_syst_catcodes_level\endcsname
\syst_catcodes_trace_pop
\advance\c_syst_catcodes_level\minusone
\fi}
@@ -243,14 +222,14 @@
\permanent\protected\def\restorecatcodes % takes previous level
{\ifnum\c_syst_catcodes_level>\plusone
- \expandafter\catcodetable\csname\??catcodetablet\number\numexpr\c_syst_catcodes_level-1\relax\endcsname
+ \catcodetable\csname\??catcodetablet\number\numexpr\c_syst_catcodes_level-1\relax\endcsname
\fi}
\permanent\protected\def\setcatcodetable#1%
{\catcodetable#1%
\syst_catcodes_trace_set}
-%D Handy for debugging:
+%D Handy for debugging (not that we ever use(d) it):
%D
%D \starttyping
%D \tracecatcodetables
diff --git a/tex/context/base/mkxl/char-ini.mkxl b/tex/context/base/mkxl/char-ini.mkxl
index 1df5d4b01..6965598a9 100644
--- a/tex/context/base/mkxl/char-ini.mkxl
+++ b/tex/context/base/mkxl/char-ini.mkxl
@@ -15,7 +15,7 @@
\registerctxluafile{char-fio}{}
\registerctxluafile{char-map}{} % maybe we will load this someplace else
-\registerctxluafile{char-tex}{}
+\registerctxluafile{char-tex}{autosuffix}
\registerctxluafile{char-ent}{}
\unprotect
@@ -52,7 +52,6 @@
\permanent \def\checkedchar {\relax\ifmmode\expandafter\checkedmathchar\else\expandafter\checkedtextchar\fi} % #1#2
\permanent \def\checkedmathchar#1#2{#2}
-%permanent \def\checkedtextchar #1{\iffontchar\font#1 \expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments\fi{\char#1}}
\permanent\protected\def\checkedtextchar #1{\clf_doifelsecharinfont\numexpr#1\relax{\char#1}} % {#2}
\newconditional\prefermathovertextchar
@@ -87,29 +86,26 @@
\normalstartimath#1#2\normalstopimath
\fi}
-%D The codes are stored in the format, so we don't need to reinitialize
-%D them (unless of course we have adapted the table). It is on the agenda
-%D to do this with \type {tex.lccode} cum suis once they're available.
-
-% \def\setcclcuc#1#2#3{\global\catcode#1=\lettercatcode\global\lccode#1=#2\global\uccode#1=#3\relax}
-% \def\setcclcucself#1{\global\catcode#1=\lettercatcode\global\lccode#1=#1\global\uccode#1=#1\relax }
+%D The codes are stored in the format, so we don't need to reinitialize them (unless
+%D of course we have adapted the table). It is on the agenda to do this with \type
+%D {tex.lccode} cum suis once they're available.
% Is setting up vrb tpa and tpb needed?
% move to lua side
-%clf_setcharactercodes
-
\clf_setlettercatcodes\texcatcodes
\clf_setlettercatcodes\ctxcatcodes
\clf_setlettercatcodes\notcatcodes
%clf_setlettercatcodes\mthcatcodes
-\clf_setlettercatcodes\vrbcatcodes
+\clf_setlettercatcodes\vrbcatcodes % hm ... we need to hyphemate
\clf_setlettercatcodes\prtcatcodes
-\clf_setlettercatcodes\tpacatcodes
-\clf_setlettercatcodes\tpbcatcodes
+\clf_setlettercatcodes\tpacatcodes % hm
+\clf_setlettercatcodes\tpbcatcodes % hm
\clf_setlettercatcodes\txtcatcodes
+\clf_setothercatcodes \rlncatcodes
+
\clf_setactivecatcodes\ctxcatcodes
\clf_setactivecatcodes\notcatcodes
\clf_setactivecatcodes\prtcatcodes
diff --git a/tex/context/base/mkxl/char-tex.lmt b/tex/context/base/mkxl/char-tex.lmt
new file mode 100644
index 000000000..5d2883f21
--- /dev/null
+++ b/tex/context/base/mkxl/char-tex.lmt
@@ -0,0 +1,763 @@
+if not modules then modules = { } end modules ['char-tex'] = {
+ version = 1.001,
+ comment = "companion to char-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local lpeg = lpeg
+local tonumber, next, type = tonumber, next, type
+local format, find, gmatch, match = string.format, string.find, string.gmatch, string.match
+local utfchar, utfbyte = utf.char, utf.byte
+local concat, tohash = table.concat, table.tohash
+local P, C, R, S, V, Cs, Cc = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.V, lpeg.Cs, lpeg.Cc
+
+local lpegpatterns = lpeg.patterns
+local lpegmatch = lpeg.match
+local utfchartabletopattern = lpeg.utfchartabletopattern
+
+local allocate = utilities.storage.allocate
+local mark = utilities.storage.mark
+
+local context = context
+local commands = commands
+
+local characters = characters
+local texcharacters = { }
+characters.tex = texcharacters
+local utffilters = characters.filters.utf
+
+local is_character = characters.is_character
+local is_letter = characters.is_letter
+local is_command = characters.is_command
+local is_spacing = characters.is_spacing
+local is_mark = characters.is_mark
+local is_punctuation = characters.is_punctuation
+
+local data = characters.data if not data then return end
+local blocks = characters.blocks
+
+local trace_defining = false trackers.register("characters.defining", function(v) characters_defining = v end)
+
+local report_defining = logs.reporter("characters")
+
+--[[ldx--
+<p>In order to deal with 8-bit output, we need to find a way to go from <l n='utf'/> to
+8-bit. This is handled in the <l n='luatex'/> engine itself.</p>
+
+<p>This leaves us problems with characters that are specific to <l n='tex'/> like
+<type>{}</type>, <type>$</type> and alike. We can remap some chars that tex input files
+are sensitive for to a private area (while writing to a utility file) and revert then
+to their original slot when we read in such a file. Instead of reverting, we can (when
+we resolve characters to glyphs) map them to their right glyph there. For this purpose
+we can use the private planes 0x0F0000 and 0x100000.</p>
+--ldx]]--
+
+local low = allocate()
+local high = allocate()
+local escapes = allocate()
+local special = "~#$%^&_{}\\|" -- "~#$%{}\\|"
+
+local private = {
+ low = low,
+ high = high,
+ escapes = escapes,
+}
+
+utffilters.private = private
+
+for ch in gmatch(special,".") do
+ local cb
+ if type(ch) == "number" then
+ cb, ch = ch, utfchar(ch)
+ else
+ cb = utfbyte(ch)
+ end
+ if cb < 256 then
+ escapes[ch] = "\\" .. ch
+ low[ch] = utfchar(0x0F0000 + cb)
+ if ch == "%" then
+ ch = "%%" -- nasty, but we need this as in replacements (also in lpeg) % is interpreted
+ end
+ high[utfchar(0x0F0000 + cb)] = ch
+ end
+end
+
+local tohigh = lpeg.replacer(low) -- frozen, only for basic tex
+local tolow = lpeg.replacer(high) -- frozen, only for basic tex
+
+lpegpatterns.utftohigh = tohigh
+lpegpatterns.utftolow = tolow
+
+function utffilters.harden(str)
+ return lpegmatch(tohigh,str)
+end
+
+function utffilters.soften(str)
+ return lpegmatch(tolow,str)
+end
+
+private.escape = utf.remapper(escapes) -- maybe: ,"dynamic"
+private.replace = utf.remapper(low) -- maybe: ,"dynamic"
+private.revert = utf.remapper(high) -- maybe: ,"dynamic"
+
+--[[ldx--
+<p>We get a more efficient variant of this when we integrate
+replacements in collapser. This more or less renders the previous
+private code redundant. The following code is equivalent but the
+first snippet uses the relocated dollars.</p>
+
+<typing>
+[󰀤x󰀤] [$x$]
+</typing>
+--ldx]]--
+
+-- using the tree-lpeg-mapper would be nice but we also need to deal with end-of-string
+-- cases: "\"\i" and don't want "\relax" to be seen as \r e lax" (for which we need to mess
+-- with spaces
+
+local accentmapping = allocate {
+ ['"'] = { [""] = "¨",
+ A = "Ä", a = "ä",
+ E = "Ë", e = "ë",
+ I = "Ï", i = "ï", ["ı"] = "ï", ["\\i"] = "ï",
+ O = "Ö", o = "ö",
+ U = "Ü", u = "ü",
+ Y = "Ÿ", y = "ÿ",
+ },
+ ["'"] = { [""] = "´",
+ A = "Á", a = "á",
+ C = "Ć", c = "ć",
+ E = "É", e = "é",
+ I = "Í", i = "í", ["ı"] = "í", ["\\i"] = "í",
+ L = "Ĺ", l = "ĺ",
+ N = "Ń", n = "ń",
+ O = "Ó", o = "ó",
+ R = "Ŕ", r = "ŕ",
+ S = "Ś", s = "ś",
+ U = "Ú", u = "ú",
+ Y = "Ý", y = "ý",
+ Z = "Ź", z = "ź",
+ },
+ ["."] = { [""] = "˙",
+ C = "Ċ", c = "ċ",
+ E = "Ė", e = "ė",
+ G = "Ġ", g = "ġ",
+ I = "İ", i = "i", ["ı"] = "i", ["\\i"] = "i",
+ Z = "Ż", z = "ż",
+ },
+ ["="] = { [""] = "¯",
+ A = "Ā", a = "ā",
+ E = "Ē", e = "ē",
+ I = "Ī", i = "ī", ["ı"] = "ī", ["\\i"] = "ī",
+ O = "Ō", o = "ō",
+ U = "Ū", u = "ū",
+ },
+ ["H"] = { [""] = "˝",
+ O = "Ő", o = "ő",
+ U = "Ű", u = "ű",
+ },
+ ["^"] = { [""] = "ˆ",
+ A = "Â", a = "â",
+ C = "Ĉ", c = "ĉ",
+ E = "Ê", e = "ê",
+ G = "Ĝ", g = "ĝ",
+ H = "Ĥ", h = "ĥ",
+ I = "Î", i = "î", ["ı"] = "î", ["\\i"] = "î",
+ J = "Ĵ", j = "ĵ",
+ O = "Ô", o = "ô",
+ S = "Ŝ", s = "ŝ",
+ U = "Û", u = "û",
+ W = "Ŵ", w = "ŵ",
+ Y = "Ŷ", y = "ŷ",
+ },
+ ["`"] = { [""] = "`",
+ A = "À", a = "à",
+ E = "È", e = "è",
+ I = "Ì", i = "ì", ["ı"] = "ì", ["\\i"] = "ì",
+ O = "Ò", o = "ò",
+ U = "Ù", u = "ù",
+ Y = "Ỳ", y = "ỳ",
+ },
+ ["c"] = { [""] = "¸",
+ C = "Ç", c = "ç",
+ K = "Ķ", k = "ķ",
+ L = "Ļ", l = "ļ",
+ N = "Ņ", n = "ņ",
+ R = "Ŗ", r = "ŗ",
+ S = "Ş", s = "ş",
+ T = "Ţ", t = "ţ",
+ },
+ ["k"] = { [""] = "˛",
+ A = "Ą", a = "ą",
+ E = "Ę", e = "ę",
+ I = "Į", i = "į",
+ U = "Ų", u = "ų",
+ },
+ ["r"] = { [""] = "˚",
+ A = "Å", a = "å",
+ U = "Ů", u = "ů",
+ },
+ ["u"] = { [""] = "˘",
+ A = "Ă", a = "ă",
+ E = "Ĕ", e = "ĕ",
+ G = "Ğ", g = "ğ",
+ I = "Ĭ", i = "ĭ", ["ı"] = "ĭ", ["\\i"] = "ĭ",
+ O = "Ŏ", o = "ŏ",
+ U = "Ŭ", u = "ŭ",
+ },
+ ["v"] = { [""] = "ˇ",
+ C = "Č", c = "č",
+ D = "Ď", d = "ď",
+ E = "Ě", e = "ě",
+ L = "Ľ", l = "ľ",
+ N = "Ň", n = "ň",
+ R = "Ř", r = "ř",
+ S = "Š", s = "š",
+ T = "Ť", t = "ť",
+ Z = "Ž", z = "ž",
+ },
+ ["~"] = { [""] = "˜",
+ A = "Ã", a = "ã",
+ I = "Ĩ", i = "ĩ", ["ı"] = "ĩ", ["\\i"] = "ĩ",
+ N = "Ñ", n = "ñ",
+ O = "Õ", o = "õ",
+ U = "Ũ", u = "ũ",
+ },
+}
+
+texcharacters.accentmapping = accentmapping
+
+local accent_map = allocate { -- incomplete
+ ['~'] = "̃" , -- ̃ Ẽ
+ ['"'] = "̈" , -- ̈ Ë
+ ["`"] = "̀" , -- ̀ È
+ ["'"] = "́" , -- ́ É
+ ["^"] = "̂" , -- ̂ Ê
+ -- ̄ Ē
+ -- ̆ Ĕ
+ -- ̇ Ė
+ -- ̉ Ẻ
+ -- ̌ Ě
+ -- ̏ Ȅ
+ -- ̑ Ȇ
+ -- ̣ Ẹ
+ -- ̧ Ȩ
+ -- ̨ Ę
+ -- ̭ Ḙ
+ -- ̰ Ḛ
+}
+
+-- local accents = concat(table.keys(accentmapping)) -- was _map
+
+local function remap_accent(a,c,braced)
+ local m = accentmapping[a]
+ if m then
+ local n = m[c]
+ if n then
+ return n
+ end
+ end
+-- local m = accent_map[a]
+-- if m then
+-- return c .. m
+-- elseif braced then -- or #c > 0
+ if braced then -- or #c > 0
+ return "\\" .. a .. "{" .. c .. "}"
+ else
+ return "\\" .. a .. " " .. c
+ end
+end
+
+local commandmapping = allocate {
+ ["aa"] = "å", ["AA"] = "Å",
+ ["ae"] = "æ", ["AE"] = "Æ",
+ ["cc"] = "ç", ["CC"] = "Ç",
+ ["i"] = "ı", ["j"] = "ȷ",
+ ["ij"] = "ij", ["IJ"] = "IJ",
+ ["l"] = "ł", ["L"] = "Ł",
+ ["o"] = "ø", ["O"] = "Ø",
+ ["oe"] = "œ", ["OE"] = "Œ",
+ ["sz"] = "ß", ["SZ"] = "SZ", ["ss"] = "ß", ["SS"] = "ß", -- uppercase: ẞ
+}
+
+texcharacters.commandmapping = commandmapping
+
+local ligaturemapping = allocate {
+ ["''"] = "”",
+ ["``"] = "“",
+ ["--"] = "–",
+ ["---"] = "—",
+}
+
+-- Older accent handling code can be found in char-def.lua but in the meantime
+-- we moved on.
+
+local untex
+
+local function toutfpattern()
+ if not untex then
+ local hash = { }
+ for k, v in next, accentmapping do
+ for kk, vv in next, v do
+ if (k >= "a" and k <= "z") or (k >= "A" and k <= "Z") then
+ hash[ "\\"..k.." "..kk ] = vv
+ hash["{\\"..k.." "..kk.."}"] = vv
+ else
+ hash["\\" ..k ..kk ] = vv
+ hash["{\\"..k ..kk.."}"] = vv
+ end
+ hash["\\" ..k.."{"..kk.."}" ] = vv
+ hash["{\\"..k.."{"..kk.."}}"] = vv
+ end
+ end
+ for k, v in next, commandmapping do
+ hash["\\"..k.." "] = v
+ hash["{\\"..k.."}"] = v
+ hash["{\\"..k.." }"] = v
+ end
+ for k, v in next, ligaturemapping do
+ hash[k] = v
+ end
+ untex = utfchartabletopattern(hash) / hash
+ end
+ return untex
+end
+
+texcharacters.toutfpattern = toutfpattern
+
+local pattern = nil
+
+local function prepare()
+ pattern = Cs((toutfpattern() + P(1))^0)
+ return pattern
+end
+
+function texcharacters.toutf(str,strip)
+ if str == "" then
+ return str
+ elseif not find(str,"\\",1,true) then
+ return str
+ -- elseif strip then
+ else
+ return lpegmatch(pattern or prepare(),str)
+ end
+end
+
+-- print(texcharacters.toutf([[\~{Z}]],true))
+-- print(texcharacters.toutf([[\'\i]],true))
+-- print(texcharacters.toutf([[\'{\i}]],true))
+-- print(texcharacters.toutf([[\"{e}]],true))
+-- print(texcharacters.toutf([[\" {e}]],true))
+-- print(texcharacters.toutf([[{\"{e}}]],true))
+-- print(texcharacters.toutf([[{\" {e}}]],true))
+-- print(texcharacters.toutf([[{\l}]],true))
+-- print(texcharacters.toutf([[{\l }]],true))
+-- print(texcharacters.toutf([[\v{r}]],true))
+-- print(texcharacters.toutf([[fo{\"o}{\ss}ar]],true))
+-- print(texcharacters.toutf([[H{\'a}n Th\^e\llap{\raise 0.5ex\hbox{\'{\relax}}} Th{\'a}nh]],true))
+
+function texcharacters.safechar(n) -- was characters.safechar
+ local c = data[n]
+ if c and c.contextname then
+ return "\\" .. c.contextname
+ else
+ return utfchar(n)
+ end
+end
+
+if not context or not commands then
+ -- used in e.g. mtx-bibtex
+ return
+end
+
+-- all kind of initializations
+
+if not interfaces then return end
+
+local implement = interfaces.implement
+
+local tex = tex
+local texsetlccode = tex.setlccode
+local texsetsfcode = tex.setsfcode
+local texsetcatcode = tex.setcatcode
+
+local contextsprint = context.sprint
+local ctxcatcodes = catcodes.numbers.ctxcatcodes
+
+local texsetmacro = tokens.setters.macro
+local texsetchar = tokens.setters.char
+
+function texcharacters.defineaccents()
+ local ctx_dodefineaccentcommand = context.dodefineaccentcommand
+ local ctx_dodefineaccent = context.dodefineaccent
+ local ctx_dodefinecommand = context.dodefinecommand
+ for accent, group in next, accentmapping do
+ ctx_dodefineaccentcommand(accent)
+ for character, mapping in next, group do
+ ctx_dodefineaccent(accent,character,mapping)
+ end
+ end
+ for command, mapping in next, commandmapping do
+ ctx_dodefinecommand(command,mapping)
+ end
+end
+
+implement { -- a waste of scanner but consistent
+ name = "defineaccents",
+ actions = texcharacters.defineaccents
+}
+
+--[[ldx--
+<p>Instead of using a <l n='tex'/> file to define the named glyphs, we
+use the table. After all, we have this information available anyway.</p>
+--ldx]]--
+
+local function to_number(s)
+ local n = tonumber(s)
+ if n then
+ return n
+ end
+ return tonumber(match(s,'^"(.*)$'),16) or 0
+end
+
+implement {
+ name = "utfchar",
+ actions = { to_number, utfchar, contextsprint },
+ arguments = "string"
+}
+
+implement {
+ name = "safechar",
+ actions = { to_number, texcharacters.safechar, contextsprint },
+ arguments = "string"
+}
+
+implement {
+ name = "uchar",
+ arguments = { "integer", "integer" },
+ actions = function(h,l)
+ context(utfchar(h*256+l))
+ end
+}
+
+tex.uprint = commands.utfchar
+
+-- in contect we don't use lc and uc codes (in fact in luatex we should have a hf code)
+-- so at some point we might drop this
+
+-- The following get set at the TeX end:
+
+local forbidden = tohash {
+ 0x000A0, -- zs nobreakspace <self>
+ 0x000AD, -- cf softhyphen <self>
+ -- 0x00600, -- cf arabicnumber <self>
+ -- 0x00601, -- cf arabicsanah <self>
+ -- 0x00602, -- cf arabicfootnotemarker <self>
+ -- 0x00603, -- cf arabicsafha <self>
+ -- 0x00604, -- cf arabicsamvat <self>
+ -- 0x00605, -- cf arabicnumberabove <self>
+ -- 0x0061C, -- cf arabiclettermark <self>
+ -- 0x006DD, -- cf arabicendofayah <self>
+ -- 0x008E2, -- cf arabicdisputedendofayah <self>
+ 0x02000, -- zs enquad <self>
+ 0x02001, -- zs emquad <self>
+ 0x02002, -- zs enspace \kern .5\emwidth
+ 0x02003, -- zs emspace \hskip \emwidth
+ 0x02004, -- zs threeperemspace <self>
+ 0x02005, -- zs fourperemspace <self>
+ 0x02006, -- zs sixperemspace <self>
+ 0x02007, -- zs figurespace <self>
+ 0x02008, -- zs punctuationspace <self>
+ 0x02009, -- zs breakablethinspace <self>
+ 0x0200A, -- zs hairspace <self>
+ 0x0200B, -- cf zerowidthspace <self>
+ 0x0200C, -- cf zwnj <self>
+ 0x0200D, -- cf zwj <self>
+ 0x0202F, -- zs narrownobreakspace <self>
+ 0x0205F, -- zs medspace \textormathspace +\medmuskip 2
+ -- 0x03000, -- zs ideographicspace <self>
+ -- 0x0FEFF, -- cf zerowidthnobreakspace \penalty \plustenthousand \kern \zeropoint
+}
+
+local csletters = characters.csletters -- also a signal that we have initialized
+local activated = { }
+local sfmode = "unset" -- unset, traditional, normal
+local block_too = false
+
+directives.register("characters.blockstoo",function(v) block_too = v end)
+
+-- If this is something that is not documentwide and used a lot, then we
+-- need a more clever approach (trivial but not now).
+
+local function setuppersfcodes(v,n)
+ if sfstate ~= "unset" then
+ report_defining("setting uppercase sf codes to %a",n)
+ for u, chr in next, data do
+ if chr.category == "lu" then
+ texsetsfcode(u,n)
+ end
+ end
+ end
+ sfstate = v
+end
+
+directives.register("characters.spaceafteruppercase",function(v)
+ if v == "traditional" then
+ setuppersfcodes(v,999)
+ elseif v == "normal" then
+ setuppersfcodes(v,1000)
+ end
+end)
+
+if not csletters then
+
+ csletters = allocate()
+ characters.csletters = csletters
+
+ report_defining("setting up character related codes and commands")
+
+ if sfstate == "unset" then
+ sfstate = "traditional"
+ end
+
+ local traditional = sfstate == "traditional"
+
+ for u, chr in next, data do -- will move up
+ local contextname = chr.contextname
+ local category = chr.category
+ local isletter = is_letter[category]
+ if contextname then
+ if is_character[category] then
+ if chr.unicodeslot < 128 then
+ if isletter then
+ local c = utfchar(u)
+ csletters[c] = u
+ end
+ else
+ local c = utfchar(u)
+ if isletter and u >= 32 and u <= 65536 then
+ csletters[c] = u
+ end
+ end
+ if isletter then
+ local lc = chr.lccode
+ local uc = chr.uccode
+ if not lc then
+ chr.lccode = u
+ lc = u
+ elseif type(lc) == "table" then
+ lc = u
+ end
+ if not uc then
+ chr.uccode = u
+ uc = u
+ elseif type(uc) == "table" then
+ uc = u
+ end
+ texsetlccode(u,lc,uc)
+ if traditional and category == "lu" then
+ texsetsfcode(code,999)
+ end
+ end
+ elseif is_command[category] and not forbidden[u] then
+ -- skip
+ elseif is_mark[category] then
+ texsetlccode(u,u,u) -- for hyphenation
+ end
+ elseif isletter then
+ csletters[utfchar(u)] = u
+ local lc, uc = chr.lccode, chr.uccode
+ if not lc then
+ chr.lccode = u
+ lc = u
+ elseif type(lc) == "table" then
+ lc = u
+ end
+ if not uc then
+ chr.uccode = u
+ uc = u
+ elseif type(uc) == "table" then
+ uc = u
+ end
+ texsetlccode(u,lc,uc)
+ if traditional and category == "lu" then
+ texsetsfcode(code,999)
+ end
+ elseif is_mark[category] then
+ texsetlccode(u,u,u) -- for hyphenation
+ end
+ end
+
+ if blocks_too then
+ -- this slows down format generation by over 10 percent
+ for k, v in next, blocks do
+ if v.catcode == "letter" then
+ local first = v.first
+ local last = v.last
+ local gaps = v.gaps
+ if first and last then
+ for u=first,last do
+ csletters[utfchar(u)] = u
+ --
+ -- texsetlccode(u,u,u) -- self self
+ --
+ end
+ end
+ if gaps then
+ for i=1,#gaps do
+ local u = gaps[i]
+ csletters[utfchar(u)] = u
+ --
+ -- texsetlccode(u,u,u) -- self self
+ --
+ end
+ end
+ end
+ end
+ end
+
+ if storage then
+ storage.register("characters/csletters", csletters, "characters.csletters")
+ end
+
+ function characters.setcharacternames(ctt)
+ for u, chr in next, data do -- will move up
+ local contextname = chr.contextname
+ local category = chr.category
+ local isletter = is_letter[category]
+ if contextname then
+ if is_character[category] then
+ if chr.unicodeslot < 128 then
+ if isletter then
+ texsetmacro(contextname,utfchar(u),"immutable")
+ else
+ texsetchar(contextname,u,"immutable")
+ end
+ else
+ texsetmacro(contextname,utfchar(u),"immutable")
+ end
+ elseif is_command[category] and not forbidden[u] then
+ texsetmacro(contextname,utfchar(u),"immutable")
+ end
+ end
+ end
+ end
+
+else
+ mark(csletters)
+end
+
+lpegpatterns.csletter = utfchartabletopattern(csletters)
+
+-- The engine presets the letters to 11 (always).
+
+function characters.setlettercatcodes(cct)
+ if trace_defining then
+ report_defining("assigning letter catcodes to catcode table %a",cct)
+ end
+ local saved = tex.catcodetable
+ tex.catcodetable = cct
+ texsetcatcode(0x200C,11) -- non-joiner
+ texsetcatcode(0x200D,11) -- joiner
+ for c, u in next, csletters do
+ texsetcatcode(u,11)
+ end
+ tex.catcodetable = saved
+end
+
+function characters.setothercatcodes(cct)
+ if trace_defining then
+ report_defining("assigning other catcodes to catcode table %a",cct)
+ end
+ local saved = tex.catcodetable
+ tex.catcodetable = cct
+ for u=65,90 do
+ texsetcatcode(u,12)
+ end
+ for u=97,122 do
+ texsetcatcode(u,12)
+ end
+ tex.catcodetable = saved
+end
+
+function characters.setactivecatcodes(cct)
+ local saved = tex.catcodetable
+ tex.catcodetable = cct
+ for i=1,#activated do
+ local u = activated[i]
+ texsetcatcode(u,13)
+ if trace_defining then
+ report_defining("character %U (%s) is active in set %a",u,data[u].description,cct)
+ end
+ end
+ tex.catcodetable = saved
+end
+
+--[[ldx--
+<p>Setting the lccodes is also done in a loop over the data table.</p>
+--ldx]]--
+
+implement {
+ name = "chardescription",
+ arguments = "integer",
+ actions = function(slot)
+ local d = data[slot]
+ if d then
+ context(d.description)
+ end
+ end,
+}
+
+if characters.setcharacternames then -- only in ini mode
+
+ implement { name = "setlettercatcodes", scope = "private", actions = characters.setlettercatcodes, arguments = "integer" }
+ implement { name = "setothercatcodes", scope = "private", actions = characters.setothercatcodes, arguments = "integer" }
+ implement { name = "setactivecatcodes", scope = "private", actions = characters.setactivecatcodes, arguments = "integer" }
+ implement { name = "setcharacternames", scope = "private", actions = characters.setcharacternames, arguments = "integer" }
+
+end
+
+-- experiment (some can move to char-ini.lua)
+
+local function overload(c,u,code,codes)
+ local c = tonumber(c)
+ if not c then
+ return
+ end
+ local u = utilities.parsers.settings_to_array(u)
+ local n = #u
+ if n == 0 then
+ return
+ end
+ local t = nil
+ if n == 1 then
+ t = tonumber(u[1])
+ else
+ t = { }
+ for i=1,n do
+ t[#t+1] = tonumber(u[i])
+ end
+ end
+ if t then
+ data[c][code] = t
+ characters[codes][c] = nil
+ end
+end
+
+interfaces.implement {
+ name = "overloaduppercase",
+ arguments = "2 strings",
+ actions = function(c,u)
+ overload(c,u,"uccode","uccodes")
+ end
+}
+
+interfaces.implement {
+ name = "overloadlowercase",
+ arguments = "2 strings",
+ actions = function(c,u)
+ overload(c,u,"lccode","lccodes")
+ end
+}
diff --git a/tex/context/base/mkxl/cont-new.mkxl b/tex/context/base/mkxl/cont-new.mkxl
index 298dbc016..c3af4c00f 100644
--- a/tex/context/base/mkxl/cont-new.mkxl
+++ b/tex/context/base/mkxl/cont-new.mkxl
@@ -13,7 +13,7 @@
% \normalend % uncomment this to get the real base runtime
-\newcontextversion{2020.12.10 22:23}
+\newcontextversion{2020.12.15 10:10}
%D This file is loaded at runtime, thereby providing an excellent place for hacks,
%D patches, extensions and new features. There can be local overloads in cont-loc
diff --git a/tex/context/base/mkxl/context.mkxl b/tex/context/base/mkxl/context.mkxl
index 238271819..39a9490a9 100644
--- a/tex/context/base/mkxl/context.mkxl
+++ b/tex/context/base/mkxl/context.mkxl
@@ -29,7 +29,7 @@
%D {YYYY.MM.DD HH:MM} format.
\immutable\edef\contextformat {\jobname}
-\immutable\edef\contextversion{2020.12.10 22:23}
+\immutable\edef\contextversion{2020.12.15 10:10}
%overloadmode 1 % check frozen / warning
%overloadmode 2 % check frozen / error
@@ -97,7 +97,6 @@
\loadmkxlfile{catc-ini}
\loadmkxlfile{catc-act}
\loadmkxlfile{catc-def}
-\loadmkxlfile{catc-ctx}
\loadmkxlfile{catc-sym}
\loadmkxlfile{toks-ini}
diff --git a/tex/context/base/mkxl/font-fbk.lmt b/tex/context/base/mkxl/font-fbk.lmt
index 0e104aca7..651bca331 100644
--- a/tex/context/base/mkxl/font-fbk.lmt
+++ b/tex/context/base/mkxl/font-fbk.lmt
@@ -189,7 +189,7 @@ local function composecharacters(tfmdata)
chr_t,
}
end
-t.depth = a_ury
+ t.depth = a_ury
elseif c_ury > a_lly then -- messy test
local dy
if compose then
@@ -220,7 +220,7 @@ t.depth = a_ury
else
dy = - deltaxheight + extraxheight
end
-t.height = a_ury-dy
+ t.height = a_ury-dy
local right = rightcommand[dx+dd]
local down = downcommand[dy]
if trace_visualize then
@@ -247,7 +247,7 @@ t.height = a_ury-dy
chr_t,
}
end
-t.height = a_ury
+ t.height = a_ury
end
end
else
@@ -316,31 +316,25 @@ registerotffeature {
description = 'unicode compat specials to ligatures',
}
-do
+-- We now provide the composer in the helpers namespace (too):
+
+fonts.helpers.composecharacters = composecharacters
+
+-- We keep this just because we have a few demos but there often are other ways to achieve
+-- the same. This code installs the builder into the regular virtual font builder, which
+-- only makes sense as demo. It also shows a bit what the evolution is.
- -- This installs the builder into the regular virtual font builder,
- -- which only makes sense as demo.
+do
local vf = handlers.vf
local commands = vf.combiner.commands
vf.helpers.composecharacters = composecharacters
- commands["compose.trace.enable"] = function()
- trace_visualize = true
- end
-
- commands["compose.trace.disable"] = function()
- trace_visualize = false
- end
-
- commands["compose.force.enable"] = function()
- force_combining = true
- end
-
- commands["compose.force.disable"] = function()
- force_combining = false
- end
+ commands["compose.trace.enable"] = function() trace_visualize = true end
+ commands["compose.trace.disable"] = function() trace_visualize = false end
+ commands["compose.force.enable"] = function() force_combining = true end
+ commands["compose.force.disable"] = function() force_combining = false end
commands["compose.trace.set"] = function(g,v)
if v[2] == nil then
diff --git a/tex/context/base/mkxl/font-ini.lmt b/tex/context/base/mkxl/font-ini.lmt
new file mode 100644
index 000000000..45e8f9e18
--- /dev/null
+++ b/tex/context/base/mkxl/font-ini.lmt
@@ -0,0 +1,40 @@
+if not modules then modules = { } end modules ['font-ini'] = {
+ 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"
+}
+
+--[[ldx--
+<p>Not much is happening here.</p>
+--ldx]]--
+
+local allocate = utilities.storage.allocate
+local sortedhash = table.sortedhash
+
+fonts = fonts or { }
+local fonts = fonts
+
+local identifiers = allocate()
+
+fonts.hashes = fonts.hashes or { identifiers = identifiers }
+fonts.tables = fonts.tables or { }
+fonts.helpers = fonts.helpers or { }
+fonts.tracers = fonts.tracers or { } -- for the moment till we have move to moduledata
+fonts.specifiers = fonts.specifiers or { } -- in format !
+
+fonts.analyzers = { } -- not needed here
+fonts.readers = { }
+fonts.definers = { methods = { } }
+fonts.loggers = { register = function() end }
+
+fonts.privateoffsets = {
+ textbase = 0xF0000, -- used for hidden (opentype features)
+ textextrabase = 0xFD000, -- used for visible by name
+ mathextrabase = 0xFE000, -- used for visible by code
+ mathbase = 0xFF000, -- used for hidden (virtual math)
+ keepnames = false, -- when set to true names are always kept (not for context)
+}
+
+-- Also here now:
diff --git a/tex/context/base/mkxl/font-lib.mklx b/tex/context/base/mkxl/font-lib.mklx
index b6daca3c7..1d7d4d1e5 100644
--- a/tex/context/base/mkxl/font-lib.mklx
+++ b/tex/context/base/mkxl/font-lib.mklx
@@ -15,7 +15,7 @@
\unprotect
-\registerctxluafile{font-ini}{}
+\registerctxluafile{font-ini}{autosuffix}
\registerctxluafile{font-log}{}
\registerctxluafile{font-con}{autosuffix}
%registerctxluafile{font-cft}{}
@@ -33,7 +33,8 @@
\registerctxluafile{font-ttf}{optimize} % cubic outlines
\registerctxluafile{font-dsp}{optimize} % ... for this one
\registerctxluafile{font-hsh}{} % hashes used by context
-\registerctxluafile{font-vfc}{}
+\registerctxluafile{font-vir}{}
+\registerctxluafile{font-vfc}{autosuffix}
\registerctxluafile{font-prv}{} % needs hashes
\registerctxluafile{font-nod}{optimize}
\registerctxluafile{font-oti}{} % otf initialization
@@ -81,7 +82,6 @@
\registerctxluafile{font-lua}{}
-\registerctxluafile{font-vir}{}
\registerctxluafile{font-enh}{autosuffix}
\registerctxluafile{good-ini}{}
diff --git a/tex/context/base/mkxl/font-vfc.lmt b/tex/context/base/mkxl/font-vfc.lmt
new file mode 100644
index 000000000..e39eca683
--- /dev/null
+++ b/tex/context/base/mkxl/font-vfc.lmt
@@ -0,0 +1,83 @@
+if not modules then modules = { } end modules ['font-vfc'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv and hand-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local select, type = select, type
+local insert = table.insert
+
+local fonts = fonts
+local helpers = fonts.helpers
+
+local setmetatableindex = table.setmetatableindex
+----- makeweak = table.makeweak
+
+-- Helpers dealing with virtual fonts: beware, these are final values so
+-- don't change the content of tables fetched from here!
+
+local push = { "push" }
+local pop = { "pop" }
+local dummy = { "comment" }
+
+function helpers.prependcommands(commands,...)
+ insert(commands,1,push)
+ for i=select("#",...),1,-1 do
+ local s = (select(i,...))
+ if s then
+ insert(commands,1,s)
+ end
+ end
+ insert(commands,pop)
+ return commands
+end
+
+function helpers.appendcommands(commands,...)
+ insert(commands,1,push)
+ insert(commands,pop)
+ for i=1,select("#",...) do
+ local s = (select(i,...))
+ if s then
+ insert(commands,s)
+ end
+ end
+ return commands
+end
+
+function helpers.prependcommandtable(commands,t)
+ insert(commands,1,push)
+ for i=#t,1,-1 do
+ local s = t[i]
+ if s then
+ insert(commands,1,s)
+ end
+ end
+ insert(commands,pop)
+ return commands
+end
+
+function helpers.appendcommandtable(commands,t)
+ insert(commands,1,push)
+ insert(commands,pop)
+ for i=1,#t do
+ local s = t[i]
+ if s then
+ insert(commands,s)
+ end
+ end
+ return commands
+end
+
+helpers.commands = utilities.storage.allocate {
+ char = setmetatableindex(function(t,k) local v = { "slot", 0, k } t[k] = v return v end),
+ right = setmetatableindex(function(t,k) local v = { "right", k } t[k] = v return v end),
+ left = setmetatableindex(function(t,k) local v = { "right", -k } t[k] = v return v end), -- todo: left
+ down = setmetatableindex(function(t,k) local v = { "down", k } t[k] = v return v end),
+ up = setmetatableindex(function(t,k) local v = { "down", -k } t[k] = v return v end), -- todo: up
+ push = push,
+ pop = pop,
+ dummy = dummy,
+}
+
diff --git a/tex/context/base/mkxl/font-vir.lmt b/tex/context/base/mkxl/font-vir.lmt
new file mode 100644
index 000000000..f60639d6a
--- /dev/null
+++ b/tex/context/base/mkxl/font-vir.lmt
@@ -0,0 +1,159 @@
+if not modules then modules = { } end modules ['font-vir'] = {
+ 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"
+}
+
+-- This is actually very experimental code but it has been so since we began with
+-- \LUATEX\ so \unknown. We keep it around because it's not that much code and
+-- because we have some demo's (in the test suite) that use it.
+
+local next, setmetatable, getmetatable = next, setmetatable, getmetatable
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+local fastcopy = table.fastcopy
+
+local fonts = fonts
+local constructors = fonts.constructors
+local vf = constructors.handlers.vf
+vf.version = 1.000 -- same as tfm
+
+local definers = fonts.definers
+local methods = definers.methods
+
+local variants = allocate()
+local combinations = { }
+local combiner = { }
+local whatever = allocate()
+local helpers = allocate()
+local predefined = fonts.helpers.commands
+
+methods.variants = variants -- todo .. wrong namespace
+vf.combinations = combinations
+vf.combiner = combiner
+vf.whatever = whatever
+vf.helpers = helpers
+vf.predefined = predefined
+
+setmetatableindex(whatever, function(t,k) local v = { } t[k] = v return v end)
+
+local function checkparameters(g,f)
+ if f and g and not g.parameters and #g.fonts > 0 then
+ local p = { }
+ for k,v in next, f.parameters do
+ p[k] = v
+ end
+ g.parameters = p
+ setmetatable(p, getmetatable(f.parameters))
+ end
+end
+
+function methods.install(tag, rules)
+ vf.combinations[tag] = rules
+ variants[tag] = function(specification)
+ return vf.combine(specification,tag)
+ end
+end
+
+local function combine_load(g,name)
+ return constructors.readanddefine(name or g.specification.name,g.specification.size)
+end
+
+local function combine_assign(g, name, from, to, start, force)
+ local f, id = combine_load(g,name)
+ if f and id then
+ -- optimize for whole range, then just g = f
+ if not from then from, to = 0, 0xFF00 end
+ if not to then to = from end
+ if not start then start = from end
+ local fc = f.characters
+ local gc = g.characters
+ local fd = f.descriptions
+ local gd = g.descriptions
+ local hn = #g.fonts+1
+ g.fonts[hn] = { id = id } -- no need to be sparse
+ for i=from,to do
+ if fc[i] and (force or not gc[i]) then
+ gc[i] = fastcopy(fc[i],true) -- can be optimized
+ gc[i].commands = { { "slot", hn, start } }
+ gd[i] = fd[i]
+ end
+ start = start + 1
+ end
+ checkparameters(g,f)
+ end
+end
+
+local function combine_process(g,list)
+ if list then
+ for _,v in next, list do
+ (combiner.commands[v[1]] or nop)(g,v)
+ end
+ end
+end
+
+local function combine_names(g,name,force)
+ local f, id = constructors.readanddefine(name,g.specification.size)
+ if f and id then
+ local fc = f.characters
+ local gc = g.characters
+ local fd = f.descriptions
+ local gd = g.descriptions
+ g.fonts[#g.fonts+1] = { id = id } -- no need to be sparse
+ local hn = #g.fonts
+ for k, v in next, fc do
+ if force or not gc[k] then
+ gc[k] = fastcopy(v,true)
+ gc[k].commands = { { "slot", hn, k } }
+ gd[i] = fd[i]
+ end
+ end
+ checkparameters(g,f)
+ end
+end
+
+local combine_feature = function(g,v)
+ local key = v[2]
+ local value = v[3]
+ if key then
+ if value == nil then
+ value = true
+ end
+ local specification = g.specification
+ if specification then
+ local normalfeatures = specification.features.normal
+ if normalfeatures then
+ normalfeatures[key] = value -- otf?
+ end
+ end
+ end
+end
+
+combiner.commands = allocate {
+ ["initialize"] = function(g,v) combine_assign (g,g.properties.name) end,
+ ["include-method"] = function(g,v) combine_process (g,combinations[v[2]]) end, -- name
+ -- ["copy-parameters"] = function(g,v) combine_parameters(g,v[2]) end, -- name
+ ["copy-range"] = function(g,v) combine_assign (g,v[2],v[3],v[4],v[5],true) end, -- name, from-start, from-end, to-start
+ ["copy-char"] = function(g,v) combine_assign (g,v[2],v[3],v[3],v[4],true) end, -- name, from, to
+ ["fallback-range"] = function(g,v) combine_assign (g,v[2],v[3],v[4],v[5],false) end, -- name, from-start, from-end, to-start
+ ["fallback-char"] = function(g,v) combine_assign (g,v[2],v[3],v[3],v[4],false) end, -- name, from, to
+ ["copy-names"] = function(g,v) combine_names (g,v[2],true) end,
+ ["fallback-names"] = function(g,v) combine_names (g,v[2],false) end,
+ ["feature"] = combine_feature,
+}
+
+function vf.combine(specification,tag)
+ local g = {
+ name = specification.name,
+ properties = { },
+ fonts = { },
+ characters = { },
+ descriptions = { },
+ specification = fastcopy(specification),
+ }
+ combine_process(g,combinations[tag])
+ return g
+end
diff --git a/tex/context/base/mkxl/grph-inc.lmt b/tex/context/base/mkxl/grph-inc.lmt
new file mode 100644
index 000000000..4fa562cb8
--- /dev/null
+++ b/tex/context/base/mkxl/grph-inc.lmt
@@ -0,0 +1,2160 @@
+if not modules then modules = { } end modules ['grph-inc'] = {
+ version = 1.001,
+ comment = "companion to grph-inc.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: in pdfe: pdfe.copyappearance(document,objnum)
+--
+-- local im = createimage { filename = fullname }
+-- local on = images.flushobject(im,document.__xrefs__[AP])
+
+-- todo: files are sometimes located twice
+-- todo: empty filename or only suffix always false (not found)
+-- lowercase types
+-- mps tex tmp svg
+-- partly qualified
+-- dimensions
+-- use metatables
+-- figures.boxnumber can go as we now can use names
+-- avoid push
+-- move some to command namespace
+
+--[[
+The ConTeXt figure inclusion mechanisms are among the oldest code
+in ConTeXt and evolved into a complex whole. One reason is that we
+deal with backend in an abstract way. What complicates matters is
+that we deal with internal graphics as well: TeX code, MetaPost code,
+etc. Later on figure databases were introduced, which resulted in
+a plug in model for locating images. On top of that runs a conversion
+mechanism (with caching) and resource logging.
+
+Porting that to Lua is not that trivial because quite some
+status information is kept between al these stages. Of course, image
+reuse also has some price, and so I decided to implement the graphics
+inclusion in several layers: detection, loading, inclusion, etc.
+
+Object sharing and scaling can happen at each stage, depending on the
+way the resource is dealt with.
+
+The TeX-Lua mix is suboptimal. This has to do with the fact that we cannot
+run TeX code from within Lua. Some more functionality will move to Lua.
+]]--
+
+-- todo: store loaded pages per pdf file someplace
+
+local tonumber, tostring, next, unpack = tonumber, tostring, next, unpack
+local format, lower, find, match, gsub = string.format, string.lower, string.find, string.match, string.gsub
+local longtostring = string.longtostring
+local contains = table.contains
+local sortedhash, sortedkeys = table.sortedhash, table.sortedkeys
+local concat, insert, remove = table.concat, table.insert, table.remove
+local todimen = string.todimen
+local collapsepath = file.collapsepath
+local formatters = string.formatters
+local odd = math.odd
+local isfile, isdir, modificationtime = lfs.isfile, lfs.isdir, lfs.modification
+
+local P, R, S, Cc, C, Cs, Ct, lpegmatch = lpeg.P, lpeg.R, lpeg.S, lpeg.Cc, lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.match
+
+local settings_to_array = utilities.parsers.settings_to_array
+local settings_to_hash = utilities.parsers.settings_to_hash
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+local replacetemplate = utilities.templates.replace
+
+local bpfactor = number.dimenfactors.bp
+
+images = images or { }
+local images = images
+
+local hasscheme = url.hasscheme
+local urlhashed = url.hashed
+
+local resolveprefix = resolvers.resolve
+
+local texgetbox = tex.getbox
+local texsetbox = tex.setbox
+
+local hpack = node.hpack
+
+local new_latelua = nodes.pool.latelua
+
+local context = context
+
+local implement = interfaces.implement
+local variables = interfaces.variables
+
+local codeinjections = backends.codeinjections
+local nodeinjections = backends.nodeinjections
+
+local trace_figures = false trackers.register ("graphics.locating", function(v) trace_figures = v end)
+local trace_bases = false trackers.register ("graphics.bases", function(v) trace_bases = v end)
+local trace_programs = false trackers.register ("graphics.programs", function(v) trace_programs = v end)
+local trace_conversion = false trackers.register ("graphics.conversion", function(v) trace_conversion = v end)
+local trace_inclusion = false trackers.register ("graphics.inclusion", function(v) trace_inclusion = v end)
+local trace_usage = false trackers.register ("graphics.usage", function(v) trace_usage = v end)
+
+local extra_check = false directives.register("graphics.extracheck", function(v) extra_check = v end)
+local auto_transform = true directives.register("graphics.autotransform", function(v) auto_transform = v end)
+
+local report = logs.reporter("graphics")
+local report_inclusion = logs.reporter("graphics","inclusion")
+
+local f_hash_part = formatters["%s->%s->%s->%s"]
+local f_hash_full = formatters["%s->%s->%s->%s->%s->%s->%s->%s"]
+
+local v_yes = variables.yes
+local v_global = variables["global"]
+local v_local = variables["local"]
+local v_default = variables.default
+local v_auto = variables.auto
+
+local maxdimen = tex.magicconstants.maxdimen -- 0x3FFFFFFF -- 2^30-1
+
+local ctx_doscalefigure = context.doscalefigure
+local ctx_relocateexternalfigure = context.relocateexternalfigure
+local ctx_startfoundexternalfigure = context.startfoundexternalfigure
+local ctx_stopfoundexternalfigure = context.stopfoundexternalfigure
+local ctx_dosetfigureobject = context.dosetfigureobject
+local ctx_doboxfigureobject = context.doboxfigureobject
+
+-- extensions
+
+function checkimage(figure)
+ if figure then
+ local width = figure.width or 0
+ local height = figure.height or 0
+ if width <= 0 or height <= 0 then
+ report_inclusion("image %a has bad dimensions (%p,%p), discarding",figure.filename or "?",width,height)
+ return false, "bad dimensions"
+ end
+ -- local xres = figure.xres
+ -- local yres = figure.yres
+ local changes = false
+ if height > width then
+ if height > maxdimen then
+ figure.height = maxdimen
+ figure.width = width * maxdimen/height
+ changed = true
+ end
+ elseif width > maxdimen then
+ figure.width = maxdimen
+ figure.height = height * maxdimen/width
+ changed = true
+ end
+ if changed then
+ report_inclusion("limiting natural dimensions of %a, old %p * %p, new %p * %p",
+ figure.filename,width,height,figure.width,figure.height)
+ end
+ if width >= maxdimen or height >= maxdimen then
+ report_inclusion("image %a is too large (%p,%p), discarding",
+ figure.filename,width,height)
+ return false, "dimensions too large"
+ end
+ return figure
+ end
+end
+
+-- This is a bit of abstraction. Fro a while we will follow the luatex image
+-- model.
+
+local imagekeys = {
+ -- only relevant ones (permitted in luatex)
+ "width", "height", "depth", "bbox",
+ "colordepth", "colorspace",
+ "filename", "filepath", "visiblefilename",
+ "imagetype", "stream",
+ "index", "objnum",
+ "pagebox", "page", "pages",
+ "rotation", "transform",
+ "xsize", "ysize", "xres", "yres",
+}
+
+local imagesizes = {
+ art = true, bleed = true, crop = true,
+ media = true, none = true, trim = true,
+}
+
+local imagetypes = { [0] =
+ "none",
+ "pdf", "png", "jpg", "jp2", "jbig2",
+ "stream",
+}
+
+imagetypes = table.swapped(imagetypes,imagetypes)
+
+images.keys = imagekeys
+images.types = imagetypes
+images.sizes = imagesizes
+
+local function createimage(specification)
+ return backends.codeinjections.newimage(specification)
+end
+
+local function copyimage(specification)
+ return backends.codeinjections.copyimage(specification)
+end
+
+local function scanimage(specification)
+ return backends.codeinjections.scanimage(specification)
+end
+
+local function embedimage(specification)
+ -- write the image to file
+ return backends.codeinjections.embedimage(specification)
+end
+
+local function wrapimage(specification)
+ -- create an image rule
+ return backends.codeinjections.wrapimage(specification)
+end
+
+images.create = createimage
+images.scan = scanimage
+images.copy = copyimage
+images.wrap = wrapimage
+images.embed = embedimage
+
+-- If really needed we can provide:
+--
+-- img = {
+-- new = createimage,
+-- scan = scanimage,
+-- copy = copyimage,
+-- node = wrapimage,
+-- write = function(specification) context(wrapimage(specification)) end,
+-- immediatewrite = embedimage,
+-- immediatewriteobject = function() end, -- not upported, experimental anyway
+-- boxes = function() return sortedkeys(imagesizes) end,
+-- fields = function() return imagekeys end,
+-- types = function() return { unpack(imagetypes,0,#imagetypes) } end,
+-- }
+
+-- end of copies / mapping
+
+local function imagetotable(imgtable)
+ if type(imgtable) == "table" then
+ return copy(imgtable)
+ end
+ local result = { }
+ for k=1,#imagekeys do
+ local key = imagekeys[k]
+ result[key] = imgtable[key]
+ end
+ return result
+end
+
+function images.serialize(i,...)
+ return table.serialize(imagetotable(i),...)
+end
+
+function images.print(i,...)
+ return table.print(imagetotable(i),...)
+end
+
+local function checkimagesize(size)
+ if size then
+ size = gsub(size,"box","")
+ return imagesizes[size] and size or "crop"
+ else
+ return "crop"
+ end
+end
+
+images.check = checkimage
+images.checksize = checkimagesize
+images.totable = imagetotable
+
+-- local indexed = { }
+--
+-- function images.ofindex(n)
+-- return indexed[n]
+-- end
+
+--- we can consider an grph-ini file
+
+figures = figures or { }
+local figures = figures
+
+figures.images = images
+figures.boxnumber = figures.boxnumber or 0
+figures.defaultsearch = true
+figures.defaultwidth = 0
+figures.defaultheight = 0
+figures.defaultdepth = 0
+figures.nofprocessed = 0
+figures.nofmissing = 0
+figures.preferquality = true -- quality over location
+
+local figures_loaded = allocate() figures.loaded = figures_loaded
+local figures_used = allocate() figures.used = figures_used
+local figures_found = allocate() figures.found = figures_found
+local figures_suffixes = allocate() figures.suffixes = figures_suffixes
+local figures_patterns = allocate() figures.patterns = figures_patterns
+local figures_resources = allocate() figures.resources = figures_resources
+
+local existers = allocate() figures.existers = existers
+local checkers = allocate() figures.checkers = checkers
+local includers = allocate() figures.includers = includers
+local remappers = allocate() figures.remappers = remappers
+local converters = allocate() figures.converters = converters
+local identifiers = allocate() figures.identifiers = identifiers
+local programs = allocate() figures.programs = programs
+
+local defaultformat = "pdf"
+local defaultprefix = "m_k_i_v_"
+
+figures.localpaths = allocate {
+ ".", "..", "../.."
+}
+
+figures.cachepaths = allocate {
+ prefix = "",
+ path = ".",
+ subpath = ".",
+}
+
+local figure_paths = allocate(table.copy(figures.localpaths))
+figures.paths = figure_paths
+
+local figures_order = allocate {
+ "pdf", "mps", "jpg", "png", "jp2", "jbig", "svg", "eps", "tif", "gif", "mov", "buffer", "tex", "cld", "auto",
+}
+
+local figures_formats = allocate { -- magic and order will move here
+ ["pdf"] = { list = { "pdf" } },
+ ["mps"] = { patterns = { "^mps$", "^%d+$" } }, -- we need to anchor
+ ["jpg"] = { list = { "jpg", "jpeg" } },
+ ["png"] = { list = { "png" } },
+ ["jp2"] = { list = { "jp2" } },
+ ["jbig"] = { list = { "jbig", "jbig2", "jb2" } },
+ ["svg"] = { list = { "svg", "svgz" } },
+ ["eps"] = { list = { "eps", "ai" } },
+ ["gif"] = { list = { "gif" } },
+ ["tif"] = { list = { "tif", "tiff" } },
+ ["mov"] = { list = { "mov", "flv", "mp4" } }, -- "avi" is not supported
+ ["buffer"] = { list = { "tmp", "buffer", "buf" } },
+ ["tex"] = { list = { "tex" } },
+ ["cld"] = { list = { "cld" } },
+ ["auto"] = { list = { "auto" } },
+}
+
+local figures_magics = allocate {
+ { format = "png", pattern = P("\137PNG\013\010\026\010") }, -- 89 50 4E 47 0D 0A 1A 0A,
+ { format = "jpg", pattern = P("\255\216\255") }, -- FF D8 FF
+ { format = "jp2", pattern = P("\000\000\000\012\106\080\032\032\013\010"), }, -- 00 00 00 0C 6A 50 20 20 0D 0A },
+ { format = "gif", pattern = P("GIF") },
+ { format = "pdf", pattern = (1 - P("%PDF"))^0 * P("%PDF") },
+}
+
+local figures_native = allocate {
+ pdf = true,
+ jpg = true,
+ jp2 = true,
+ png = true,
+}
+
+figures.formats = figures_formats -- frozen
+figures.magics = figures_magics -- frozen
+figures.order = figures_order -- frozen
+
+-- name checker
+
+local okay = P("m_k_i_v_")
+
+local pattern = (R("az","AZ") * P(":"))^-1 * ( -- a-z : | A-Z :
+ (okay + R("az","09") + S("_/") - P("_")^2)^1 * (P(".") * R("az")^1)^0 * P(-1) + -- a-z | single _ | /
+ (okay + R("az","09") + S("-/") - P("-")^2)^1 * (P(".") * R("az")^1)^0 * P(-1) + -- a-z | single - | /
+ (okay + R("AZ","09") + S("_/") - P("_")^2)^1 * (P(".") * R("AZ")^1)^0 * P(-1) + -- A-Z | single _ | /
+ (okay + R("AZ","09") + S("-/") - P("-")^2)^1 * (P(".") * R("AZ")^1)^0 * P(-1) -- A-Z | single - | /
+) * Cc(false) + Cc(true)
+
+function figures.badname(name)
+ if not name then
+ -- bad anyway
+ elseif not hasscheme(name) then
+ return lpegmatch(pattern,name)
+ else
+ return lpegmatch(pattern,file.basename(name))
+ end
+end
+
+logs.registerfinalactions(function()
+ local done = false
+ if trace_usage and figures.nofprocessed > 0 then
+ logs.startfilelogging(report,"names")
+ for _, data in sortedhash(figures_found) do
+ if done then
+ report()
+ else
+ done = true
+ end
+ report("asked : %s",data.askedname)
+ if data.found then
+ report("format : %s",data.format)
+ report("found : %s",data.foundname)
+ report("used : %s",data.fullname)
+ if data.badname then
+ report("comment : %s","bad name")
+ elseif data.comment then
+ report("comment : %s",data.comment)
+ end
+ else
+ report("comment : %s","not found")
+ end
+ end
+ logs.stopfilelogging()
+ end
+ if figures.nofmissing > 0 and logs.loggingerrors() then
+ logs.starterrorlogging(report,"missing figures")
+ for _, data in sortedhash(figures_found) do
+ report("%w%s",6,data.askedname)
+ end
+ logs.stoperrorlogging()
+ end
+end)
+
+-- We can set the order but only indirectly so that we can check for support.
+
+function figures.setorder(list) -- can be table or string
+ if type(list) == "string" then
+ list = settings_to_array(list)
+ end
+ if list and #list > 0 then
+ figures_order = allocate()
+ figures.order = figures_order
+ local done = { } -- just to be sure in case the list is generated
+ for i=1,#list do
+ local l = lower(list[i])
+ if figures_formats[l] and not done[l] then
+ figures_order[#figures_order+1] = l
+ done[l] = true
+ end
+ end
+ report_inclusion("lookup order % a",figures_order)
+ else
+ -- invalid list
+ end
+end
+
+local function guessfromstring(str)
+ if str then
+ for i=1,#figures_magics do
+ local pattern = figures_magics[i]
+ if lpegmatch(pattern.pattern,str) then
+ local format = pattern.format
+ if trace_figures then
+ report_inclusion("file %a has format %a",filename,format)
+ end
+ return format
+ end
+ end
+ end
+end
+
+figures.guessfromstring = guessfromstring
+
+function figures.guess(filename)
+ local f = io.open(filename,'rb')
+ if f then
+ local str = f:read(100)
+ f:close()
+ if str then
+ return guessfromstring(str)
+ end
+ end
+end
+
+local function setlookups() -- tobe redone .. just set locals
+ figures_suffixes = allocate()
+ figures_patterns = allocate()
+ for _, format in next, figures_order do
+ local data = figures_formats[format]
+ local list = data.list
+ if list then
+ for i=1,#list do
+ figures_suffixes[list[i]] = format -- hash
+ end
+ else
+ figures_suffixes[format] = format
+ end
+ local patterns = data.patterns
+ if patterns then
+ for i=1,#patterns do
+ figures_patterns[#figures_patterns+1] = { patterns[i], format } -- array
+ end
+ end
+ end
+ figures.suffixes = figures_suffixes
+ figures.patterns = figures_patterns
+end
+
+setlookups()
+
+figures.setlookups = setlookups
+
+function figures.registerresource(t)
+ local n = #figures_resources + 1
+ figures_resources[n] = t
+ return n
+end
+
+local function register(tag,what,target)
+ local data = figures_formats[target] -- resolver etc
+ if not data then
+ data = { }
+ figures_formats[target] = data
+ end
+ local d = data[tag] -- list or pattern
+ if d and not contains(d,what) then
+ d[#d+1] = what -- suffix or patternspec
+ else
+ data[tag] = { what }
+ end
+ if not contains(figures_order,target) then
+ figures_order[#figures_order+1] = target
+ end
+ setlookups()
+end
+
+function figures.registersuffix (suffix, target) register('list',suffix,target) end
+function figures.registerpattern(pattern,target) register('pattern',pattern,target) end
+
+implement { name = "registerfiguresuffix", actions = register, arguments = { "'list'", "string", "string" } }
+implement { name = "registerfigurepattern", actions = register, arguments = { "'pattern'", "string", "string" } }
+
+local last_locationset = last_locationset or nil
+local last_pathlist = last_pathlist or nil
+
+function figures.setpaths(locationset,pathlist)
+ if last_locationset == locationset and last_pathlist == pathlist then
+ -- this function can be called each graphic so we provide this optimization
+ return
+ end
+ local t, h = figure_paths, settings_to_hash(locationset)
+ if last_locationset ~= locationset then
+ -- change == reset (actually, a 'reset' would indeed reset
+ if h[v_local] then
+ t = table.fastcopy(figures.localpaths or { })
+ else
+ t = { }
+ end
+ figures.defaultsearch = h[v_default]
+ last_locationset = locationset
+ end
+ if h[v_global] then
+ local list = settings_to_array(pathlist)
+ for i=1,#list do
+ local s = list[i]
+ if not contains(t,s) then
+ t[#t+1] = s
+ end
+ end
+ end
+ figure_paths = t
+ last_pathlist = pathlist
+ figures.paths = figure_paths
+ if trace_figures then
+ report_inclusion("using locations %a",last_locationset)
+ report_inclusion("using paths % a",figure_paths)
+ end
+end
+
+implement { name = "setfigurepaths", actions = figures.setpaths, arguments = "2 strings" }
+
+-- check conversions and handle it here
+
+function figures.hash(data)
+ local status = data and data.status
+ return (status and status.hash or tostring(status.private)) or "nohash" -- the <img object>
+end
+
+-- interfacing to tex
+
+local function new() -- we could use metatables status -> used -> request but it needs testing
+ local request = {
+ name = false,
+ label = false,
+ format = false,
+ page = false,
+ width = false,
+ height = false,
+ preview = false,
+ ["repeat"] = false,
+ controls = false,
+ display = false,
+ mask = false,
+ conversion = false,
+ resolution = false,
+ color = false,
+ arguments = false,
+ cache = false,
+ prefix = false,
+ size = false,
+ }
+ local used = {
+ fullname = false,
+ format = false,
+ name = false,
+ path = false,
+ suffix = false,
+ width = false,
+ height = false,
+ }
+ local status = {
+ status = 0,
+ converted = false,
+ cached = false,
+ fullname = false,
+ format = false,
+ }
+ -- this needs checking because we might check for nil, the test case
+ -- is getfiguredimensions which then should return ~= 0
+ -- setmetatableindex(status, used)
+ -- setmetatableindex(used, request)
+ return {
+ request = request,
+ used = used,
+ status = status,
+ }
+end
+
+-- use table.insert|remove
+
+local lastfiguredata = nil -- will be topofstack or last so no { } (else problems with getfiguredimensions)
+local callstack = { }
+
+function figures.initialize(request)
+ local figuredata = new()
+ if request then
+ -- request.width/height are strings and are only used when no natural dimensions
+ -- can be determined; at some point the handlers might set them to numbers instead
+ local w = tonumber(request.width) or 0
+ local h = tonumber(request.height) or 0
+ local p = tonumber(request.page) or 0
+ request.width = w > 0 and w or nil
+ request.height = h > 0 and h or nil
+ --
+ request.page = p > 0 and p or 1
+ request.keepopen = p > 0
+ request.size = checkimagesize(request.size)
+ request.object = request.object == v_yes
+ request["repeat"] = request["repeat"] == v_yes
+ request.preview = request.preview == v_yes
+ request.cache = request.cache ~= "" and request.cache
+ request.prefix = request.prefix ~= "" and request.prefix
+ request.format = request.format ~= "" and request.format
+ request.compact = request.compact == v_yes
+ table.merge(figuredata.request,request)
+ end
+ return figuredata
+end
+
+function figures.push(request)
+ statistics.starttiming(figures)
+ local figuredata = figures.initialize(request) -- we could use table.sparse but we set them later anyway
+ insert(callstack,figuredata)
+ lastfiguredata = figuredata
+ return figuredata
+end
+
+function figures.pop()
+ remove(callstack)
+ lastfiguredata = callstack[#callstack] or lastfiguredata
+ statistics.stoptiming(figures)
+end
+
+function figures.current()
+ return callstack[#callstack] or lastfiguredata
+end
+
+local function get(category,tag,default)
+ local value = lastfiguredata and lastfiguredata[category]
+ value = value and value[tag]
+ if not value or value == "" or value == true then
+ return default or ""
+ else
+ return value
+ end
+end
+
+local function setdimensions(box)
+ local status = lastfiguredata and lastfiguredata.status
+ local used = lastfiguredata and lastfiguredata.used
+ if status and used then
+ local b = texgetbox(box)
+ local w = b.width
+ local h = b.height + b.depth
+ status.width = w
+ status.height = h
+ used.width = w
+ used.height = h
+ status.status = 10
+ end
+end
+
+figures.get = get
+figures.set = setdimensions
+
+implement { name = "figurestatus", actions = { get, context }, arguments = { "'status'", "string", "string" } }
+implement { name = "figurerequest", actions = { get, context }, arguments = { "'request'", "string", "string" } }
+implement { name = "figureused", actions = { get, context }, arguments = { "'used'", "string", "string" } }
+
+implement { name = "figurefilepath", public = true, actions = { get, file.dirname, context }, arguments = { "'used'", "'fullname'" } }
+implement { name = "figurefilename", public = true, actions = { get, file.nameonly, context }, arguments = { "'used'", "'fullname'" } }
+implement { name = "figurefiletype", public = true, actions = { get, file.extname, context }, arguments = { "'used'", "'fullname'" } }
+
+implement { name = "figuresetdimensions", actions = setdimensions, arguments = "integer" }
+
+-- todo: local path or cache path
+
+local function forbiddenname(filename)
+ if not filename or filename == "" then
+ return false
+ end
+ local expandedfullname = collapsepath(filename,true)
+ local expandedinputname = collapsepath(file.addsuffix(environment.jobfilename,environment.jobfilesuffix),true)
+ if expandedfullname == expandedinputname then
+ report_inclusion("skipping graphic with same name as input filename %a, enforce suffix",expandedinputname)
+ return true
+ end
+ local expandedoutputname = collapsepath(codeinjections.getoutputfilename(),true)
+ if expandedfullname == expandedoutputname then
+ report_inclusion("skipping graphic with same name as output filename %a, enforce suffix",expandedoutputname)
+ return true
+ end
+end
+
+local function rejected(specification)
+ if extra_check then
+ local fullname = specification.fullname
+ if fullname and figures_native[file.suffix(fullname)] and not figures.guess(fullname) then
+ specification.comment = "probably a bad file"
+ specification.found = false
+ specification.error = true
+ report_inclusion("file %a looks bad",fullname)
+ return true
+ end
+ end
+end
+
+local function wipe(str)
+ if str == "" or str == "default" or str == "unknown" then
+ return nil
+ else
+ return str
+ end
+end
+
+local function register(askedname,specification)
+ if not specification then
+ specification = { askedname = askedname, comment = "invalid specification" }
+ elseif forbiddenname(specification.fullname) then
+ specification = { askedname = askedname, comment = "forbidden name" }
+ elseif specification.internal then
+ -- no filecheck needed
+ specification.found = true
+ if trace_figures then
+ report_inclusion("format %a internally supported by engine",specification.format)
+ end
+ elseif not rejected(specification) then
+ local format = specification.format
+ if format then
+ local conversion = wipe(specification.conversion)
+ local resolution = wipe(specification.resolution)
+ local arguments = wipe(specification.arguments)
+ local newformat = conversion
+ if not newformat or newformat == "" then
+ newformat = defaultformat
+ end
+ if trace_conversion then
+ report_inclusion("checking conversion of %a, fullname %a, old format %a, new format %a, conversion %a, resolution %a, arguments %a",
+ askedname,
+ specification.fullname,
+ format,
+ newformat,
+ conversion or "default",
+ resolution or "default",
+ arguments or ""
+ )
+ end
+ -- begin of quick hack
+ local remapper = remappers[format]
+ if remapper then
+ remapper = remapper[conversion]
+ if remapper then
+ specification = remapper(specification) or specification
+ format = specification.format
+ newformat = format
+ conversion = nil
+ end
+ end
+ -- end of quick hack
+ local converter = (not remapper) and (newformat ~= format or resolution or arguments) and converters[format]
+ if converter then
+ local okay = converter[newformat]
+ if okay then
+ converter = okay
+ else
+ newformat = defaultformat
+ converter = converter[newformat]
+ end
+ elseif trace_conversion then
+ report_inclusion("no converter for %a to %a",format,newformat)
+ end
+ if converter then
+ -- todo: make this a function
+ --
+ -- todo: outline as helper function
+ --
+ local oldname = specification.fullname
+ local newpath = file.dirname(oldname)
+ local oldbase = file.basename(oldname)
+ --
+ -- problem: we can have weird filenames, like a.b.c (no suffix) and a.b.c.gif
+ -- so we cannot safely remove a suffix (unless we do that for known suffixes)
+ --
+ -- local newbase = file.removesuffix(oldbase) -- assumes a known suffix
+ --
+ -- so we now have (also see *):
+ --
+ local newbase = oldbase
+ --
+ local fc = specification.cache or figures.cachepaths.path
+ if fc and fc ~= "" and fc ~= "." then
+ newpath = gsub(fc,"%*",newpath) -- so cachedir can be "/data/cache/*"
+ else
+ newbase = defaultprefix .. newbase
+ end
+ local subpath = specification.subpath or figures.cachepaths.subpath
+ if subpath and subpath ~= "" and subpath ~= "." then
+ newpath = newpath .. "/" .. subpath
+ end
+ if not isdir(newpath) then
+ dir.makedirs(newpath)
+ if not file.is_writable(newpath) then
+ if trace_conversion then
+ report_inclusion("path %a is not writable, forcing conversion path %a",newpath,".")
+ end
+ newpath = "."
+ end
+ end
+ local prefix = specification.prefix or figures.cachepaths.prefix
+ if prefix and prefix ~= "" then
+ newbase = prefix .. newbase
+ end
+ local hash = ""
+ if resolution then
+ hash = hash .. "[r:" .. resolution .. "]"
+ end
+ if arguments then
+ hash = hash .. "[a:" .. arguments .. "]"
+ end
+ if hash ~= "" then
+ newbase = newbase .. "_" .. md5.hex(hash)
+ end
+ --
+ -- see *, we had:
+ --
+ -- local newbase = file.addsuffix(newbase,newformat)
+ --
+ -- but now have (result of Aditya's web image testing):
+ --
+ -- as a side effect we can now have multiple fetches with different
+ -- original figures_formats, not that it matters much (apart from older conversions
+ -- sticking around)
+ --
+ local newbase = newbase .. "." .. newformat
+ local newname = file.join(newpath,newbase)
+ oldname = collapsepath(oldname)
+ newname = collapsepath(newname)
+ local oldtime = modificationtime(oldname) or 0
+ local newtime = modificationtime(newname) or 0
+ if newtime == 0 or oldtime > newtime then
+ if trace_conversion then
+ report_inclusion("converting %a (%a) from %a to %a",askedname,oldname,format,newformat)
+ end
+ converter(oldname,newname,resolution or "", arguments or "")
+ else
+ if trace_conversion then
+ report_inclusion("no need to convert %a (%a) from %a to %a",askedname,oldname,format,newformat)
+ end
+ end
+ if io.exists(newname) and io.size(newname) > 0 then
+ specification.foundname = oldname
+ specification.fullname = newname
+ specification.prefix = prefix
+ specification.subpath = subpath
+ specification.converted = true
+ format = newformat
+ if not figures_suffixes[format] then
+ -- maybe the new format is lowres.png (saves entry in suffixes)
+ -- so let's do this extra check
+ local suffix = file.suffix(newformat)
+ if figures_suffixes[suffix] then
+ if trace_figures then
+ report_inclusion("using suffix %a as format for %a",suffix,format)
+ end
+ format = suffix
+ end
+ end
+ specification.format = format
+ elseif io.exists(oldname) then
+ report_inclusion("file %a is bugged",oldname)
+ if format and imagetypes[format] then
+ specification.fullname = oldname
+ end
+ specification.converted = false
+ specification.bugged = true
+ end
+ end
+ end
+ if format then
+ local found = figures_suffixes[format]
+ if not found then
+ specification.found = false
+ if trace_figures then
+ report_inclusion("format %a is not supported",format)
+ end
+ elseif imagetypes[format] then
+ specification.found = true
+ if trace_figures then
+ report_inclusion("format %a natively supported by backend",format)
+ end
+ else
+ specification.found = true -- else no foo.1 mps conversion
+ if trace_figures then
+ report_inclusion("format %a supported by output file format",format)
+ end
+ end
+ else
+ specification.askedname = askedname
+ specification.found = false
+ end
+ end
+ if specification.found then
+ specification.foundname = specification.foundname or specification.fullname
+ else
+ specification.foundname = nil
+ end
+ specification.badname = figures.badname(askedname)
+ local askedhash = f_hash_part(
+ askedname,
+ specification.conversion or "default",
+ specification.resolution or "default",
+ specification.arguments or ""
+ )
+ figures_found[askedhash] = specification
+ if not specification.found then
+ figures.nofmissing = figures.nofmissing + 1
+ end
+ return specification
+end
+
+local resolve_too = false -- true
+
+local internalschemes = {
+ file = true,
+ tree = true,
+ dirfile = true,
+ dirtree = true,
+}
+
+local function locate(request) -- name, format, cache
+ -- not resolvers.cleanpath(request.name) as it fails on a!b.pdf and b~c.pdf
+ -- todo: more restricted cleanpath
+ local askedname = request.name or ""
+ local askedcache = request.cache
+ local askedconversion = request.conversion
+ local askedresolution = request.resolution
+ local askedarguments = request.arguments
+ local askedhash = f_hash_part(
+ askedname,
+ askedconversion or "default",
+ askedresolution or "default",
+ askedarguments or ""
+ )
+ local foundname = figures_found[askedhash]
+ if foundname then
+ return foundname
+ end
+ --
+ --
+ local askedformat = request.format
+ if not askedformat or askedformat == "" or askedformat == "unknown" then
+ askedformat = file.suffix(askedname) or ""
+ elseif askedformat == v_auto then
+ if trace_figures then
+ report_inclusion("ignoring suffix of %a",askedname)
+ end
+ askedformat = ""
+ askedname = file.removesuffix(askedname)
+ end
+ -- protocol check
+ local hashed = urlhashed(askedname)
+ if not hashed then
+ -- go on
+ elseif internalschemes[hashed.scheme] then
+ local path = hashed.path
+ if path and path ~= "" then
+ askedname = path
+ end
+ else
+ local foundname = resolvers.findbinfile(askedname)
+ if not foundname or not isfile(foundname) then -- foundname can be dummy
+ if trace_figures then
+ report_inclusion("unknown url %a",askedname)
+ end
+ -- url not found
+ return register(askedname)
+ end
+ local guessedformat = figures.guess(foundname)
+ if askedformat ~= guessedformat then
+ if trace_figures then
+ report_inclusion("url %a has unknown format",askedname)
+ end
+ -- url found, but wrong format
+ return register(askedname)
+ else
+ if trace_figures then
+ report_inclusion("url %a is resolved to %a",askedname,foundname)
+ end
+ return register(askedname, {
+ askedname = askedname,
+ fullname = foundname,
+ format = askedformat,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+ end
+ end
+ -- we could use the hashed data instead
+ local askedpath = file.is_rootbased_path(askedname)
+ local askedbase = file.basename(askedname)
+ if askedformat ~= "" then
+ askedformat = lower(askedformat)
+ if trace_figures then
+ report_inclusion("forcing format %a",askedformat)
+ end
+ local format = figures_suffixes[askedformat]
+ if not format then
+ for i=1,#figures_patterns do
+ local pattern = figures_patterns[i]
+ if find(askedformat,pattern[1]) then
+ format = pattern[2]
+ if trace_figures then
+ report_inclusion("asked format %a matches %a",askedformat,pattern[1])
+ end
+ break
+ end
+ end
+ end
+ if format then
+ local foundname, quitscanning, forcedformat, internal = figures.exists(askedname,format,resolve_too) -- not askedformat
+ if foundname then
+ return register(askedname, {
+ askedname = askedname,
+ fullname = foundname, -- askedname,
+ format = forcedformat or format,
+ cache = askedcache,
+ -- foundname = foundname, -- no
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ internal = internal,
+ })
+ elseif quitscanning then
+ return register(askedname)
+ end
+ askedformat = format -- new per 2013-08-05
+ elseif trace_figures then
+ report_inclusion("unknown format %a",askedformat)
+ end
+ if askedpath then
+ -- path and type given, todo: strip pieces of path
+ local foundname, quitscanning, forcedformat = figures.exists(askedname,askedformat,resolve_too)
+ if foundname then
+ return register(askedname, {
+ askedname = askedname,
+ fullname = foundname, -- askedname,
+ format = forcedformat or askedformat,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+ end
+ else
+ -- type given
+ for i=1,#figure_paths do
+ local path = resolveprefix(figure_paths[i]) -- we resolve (e.g. jobfile:)
+ local check = path .. "/" .. askedname
+ -- we pass 'true' as it can be an url as well, as the type
+ -- is given we don't waste much time
+ local foundname, quitscanning, forcedformat = figures.exists(check,askedformat,resolve_too)
+ if foundname then
+ return register(check, {
+ askedname = askedname,
+ fullname = foundname, -- check,
+ format = askedformat,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+ end
+ end
+ if figures.defaultsearch then
+ local check = resolvers.findfile(askedname)
+ if check and check ~= "" then
+ return register(askedname, {
+ askedname = askedname,
+ fullname = check,
+ format = askedformat,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+ end
+ end
+ end
+ elseif askedpath then
+ if trace_figures then
+ report_inclusion("using rootbased path")
+ end
+ for i=1,#figures_order do
+ local format = figures_order[i]
+ local list = figures_formats[format].list or { format }
+ for j=1,#list do
+ local suffix = list[j]
+ local check = file.addsuffix(askedname,suffix)
+ local foundname, quitscanning, forcedformat = figures.exists(check,format,resolve_too)
+ if foundname then
+ return register(askedname, {
+ askedname = askedname,
+ fullname = foundname, -- check,
+ format = forcedformat or format,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+ end
+ end
+ end
+ else
+ if figures.preferquality then
+ if trace_figures then
+ report_inclusion("unknown format, quality preferred")
+ end
+ for j=1,#figures_order do
+ local format = figures_order[j]
+ local list = figures_formats[format].list or { format }
+ for k=1,#list do
+ local suffix = list[k]
+ -- local name = file.replacesuffix(askedbase,suffix)
+ local name = file.replacesuffix(askedname,suffix)
+ for i=1,#figure_paths do
+ local path = resolveprefix(figure_paths[i]) -- we resolve (e.g. jobfile:)
+ local check = path .. "/" .. name
+ local isfile = internalschemes[urlhashed(check).scheme]
+ if not isfile then
+ if trace_figures then
+ report_inclusion("warning: skipping path %a",path)
+ end
+ else
+ local foundname, quitscanning, forcedformat = figures.exists(check,format,resolve_too) -- true)
+ if foundname then
+ return register(askedname, {
+ askedname = askedname,
+ fullname = foundname, -- check
+ format = forcedformat or format,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments
+ })
+ end
+ end
+ end
+ end
+ end
+ else -- 'location'
+ if trace_figures then
+ report_inclusion("unknown format, using path strategy")
+ end
+ for i=1,#figure_paths do
+ local path = resolveprefix(figure_paths[i]) -- we resolve (e.g. jobfile:)
+ for j=1,#figures_order do
+ local format = figures_order[j]
+ local list = figures_formats[format].list or { format }
+ for k=1,#list do
+ local suffix = list[k]
+ local check = path .. "/" .. file.replacesuffix(askedbase,suffix)
+ local foundname, quitscanning, forcedformat = figures.exists(check,format,resolve_too)
+ if foundname then
+ return register(askedname, {
+ askedname = askedname,
+ fullname = foudname, -- check,
+ format = forcedformat or format,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+ end
+ end
+ end
+ end
+ end
+ if figures.defaultsearch then
+ if trace_figures then
+ report_inclusion("using default tex path")
+ end
+ for j=1,#figures_order do
+ local format = figures_order[j]
+ local list = figures_formats[format].list or { format }
+ for k=1,#list do
+ local suffix = list[k]
+ local check = resolvers.findfile(file.replacesuffix(askedname,suffix))
+ if check and check ~= "" then
+ return register(askedname, {
+ askedname = askedname,
+ fullname = check,
+ format = format,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+ end
+ end
+ end
+ end
+ end
+ return register(askedname, { -- these two are needed for hashing 'found'
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+end
+
+-- -- -- plugins -- -- --
+
+function identifiers.default(data)
+ local dr, du, ds = data.request, data.used, data.status
+ local l = locate(dr)
+ local foundname = l.foundname
+ local fullname = l.fullname or foundname
+ if fullname then
+ du.format = l.format or false
+ du.fullname = fullname -- can be cached
+ ds.fullname = foundname -- original
+ ds.format = l.format
+ ds.status = (l.bugged and 0) or (l.found and 10) or 0
+ end
+ return data
+end
+
+function figures.identify(data)
+ data = data or callstack[#callstack] or lastfiguredata
+ if data then
+ local list = identifiers.list -- defined at the end
+ for i=1,#list do
+ local identifier = list[i]
+ local data = identifier(data)
+-- if data and (not data.status and data.status.status > 0) then
+ if data and (not data.status and data.status.status > 0) then
+ break
+ end
+ end
+ end
+ return data
+end
+
+function figures.exists(askedname,format,resolve)
+ return (existers[format] or existers.generic)(askedname,resolve)
+end
+
+function figures.check(data)
+ data = data or callstack[#callstack] or lastfiguredata
+ return (checkers[data.status.format] or checkers.generic)(data)
+end
+
+local used_images = { }
+
+statistics.register("used graphics",function()
+ if trace_usage then
+ local filename = file.nameonly(environment.jobname) .. "-figures-usage.lua"
+ if next(figures_found) then
+ local found = { }
+ for _, data in sortedhash(figures_found) do
+ found[#found+1] = data
+ for k, v in next, data do
+ if v == false or v == "" then
+ data[k] = nil
+ end
+ end
+ end
+ for i=1,#used_images do
+ local u = used_images[i]
+ local s = u.status
+ if s then
+ s.status = nil -- doesn't say much here
+ if s.error then
+ u.used = { } -- better show that it's not used
+ end
+ end
+ for _, t in next, u do
+ for k, v in next, t do
+ if v == false or v == "" or k == "private" then
+ t[k] = nil
+ end
+ end
+ end
+ end
+ table.save(filename,{
+ found = found,
+ used = used_images,
+ } )
+ return format("log saved in '%s'",filename)
+ else
+ os.remove(filename)
+ end
+ end
+end)
+
+function figures.include(data)
+ data = data or callstack[#callstack] or lastfiguredata
+ if trace_usage then
+ used_images[#used_images+1] = data
+ end
+ return (includers[data.status.format] or includers.generic)(data)
+end
+
+function figures.scale(data) -- will become lua code
+ data = data or callstack[#callstack] or lastfiguredata
+ ctx_doscalefigure()
+ return data
+end
+
+function figures.done(data)
+ figures.nofprocessed = figures.nofprocessed + 1
+ data = data or callstack[#callstack] or lastfiguredata
+ local dr, du, ds, nr = data.request, data.used, data.status, figures.boxnumber
+ local box = texgetbox(nr)
+ ds.width = box.width
+ ds.height = box.height
+ -- somehow this fails on some of tacos files
+ -- ds.xscale = ds.width /(du.width or 1)
+ -- ds.yscale = ds.height/(du.height or 1)
+ -- du.width and du.height can be false
+ if du.width and du.height and du.width > 0 and du.height > 0 then
+ ds.xscale = ds.width /du.width
+ ds.yscale = ds.height/du.height
+ elseif du.xsize and du.ysize and du.xsize > 0 and du.ysize > 0 then
+ ds.xscale = ds.width /du.xsize
+ ds.yscale = ds.height/du.ysize
+ else
+ ds.xscale = 1
+ ds.yscale = 1
+ end
+ -- sort of redundant but can be limited
+ ds.page = ds.page or du.page or dr.page
+ return data
+end
+
+function figures.dummy(data)
+ data = data or callstack[#callstack] or lastfiguredata
+ local dr, du, nr = data.request, data.used, figures.boxnumber
+ local box = hpack(node.new("hlist")) -- we need to set the dir (luatex 0.60 buglet)
+ du.width = du.width or figures.defaultwidth
+ du.height = du.height or figures.defaultheight
+ du.depth = du.depth or figures.defaultdepth
+ box.width = du.width
+ box.height = du.height
+ box.depth = du.depth
+ texsetbox(nr,box) -- hm, should be global (to be checked for consistency)
+end
+
+-- -- -- generic -- -- --
+
+function existers.generic(askedname,resolve)
+ -- not findbinfile
+ local result
+ if hasscheme(askedname) then
+ result = resolvers.findbinfile(askedname)
+ elseif isfile(askedname) then
+ result = askedname
+ elseif resolve then
+ result = resolvers.findbinfile(askedname)
+ end
+ if not result or result == "" then
+ result = false
+ end
+ if trace_figures then
+ if result then
+ report_inclusion("%a resolved to %a",askedname,result)
+ else
+ report_inclusion("%a cannot be resolved",askedname)
+ end
+ end
+ return result
+end
+
+-- pdf : 0-3: 0 90 180 270
+-- jpeg: 0 unset 1-4: 0 90 180 270 5-8: flipped r/c
+
+local transforms = setmetatableindex (
+ {
+ ["orientation-1"] = 0, ["R0"] = 0,
+ ["orientation-2"] = 4, ["R0MH"] = 4,
+ ["orientation-3"] = 2, ["R180"] = 2,
+ ["orientation-4"] = 6, ["R0MV"] = 6,
+ ["orientation-5"] = 5, ["R270MH"] = 5,
+ ["orientation-6"] = 3, ["R90"] = 3,
+ ["orientation-7"] = 7, ["R90MH"] = 7,
+ ["orientation-8"] = 1, ["R270"] = 1,
+ },
+ function(t,k) -- transforms are 0 .. 7
+ local v = tonumber(k) or 0
+ if v < 0 or v > 7 then
+ v = 0
+ end
+ t[k] = v
+ return v
+ end
+)
+
+local function checktransform(figure,forced)
+ if auto_transform then
+
+ local orientation = (forced ~= "" and forced ~= v_auto and forced) or figure.orientation or 0
+ local transform = transforms["orientation-"..orientation]
+ figure.transform = transform
+ if odd(transform) then
+ return figure.height, figure.width
+ else
+ return figure.width, figure.height
+ end
+ end
+end
+
+local pagecount = { }
+
+function checkers.generic(data)
+ local dr, du, ds = data.request, data.used, data.status
+ local name = du.fullname or "unknown generic"
+ local page = du.page or dr.page
+ local size = dr.size or "crop"
+ local color = dr.color or "natural"
+ local mask = dr.mask or "none"
+ local conversion = dr.conversion
+ local resolution = dr.resolution
+ local arguments = dr.arguments
+ local scanimage = dr.scanimage or scanimage
+ local userpassword = dr.userpassword
+ local ownerpassword = dr.ownerpassword
+ if not conversion or conversion == "" then
+ conversion = "default"
+ end
+ if not resolution or resolution == "" then
+ resolution = "default"
+ end
+ if not arguments or arguments == "" then
+ arguments = "default"
+ end
+ local hash = f_hash_full(
+ name,
+ page,
+ size,
+ color,
+ mask,
+ conversion,
+ resolution,
+ arguments
+ )
+ --
+ local figure = figures_loaded[hash]
+ if figure == nil then
+ figure = createimage {
+ filename = name,
+ page = page,
+ pagebox = dr.size,
+ keepopen = dr.keepopen or false,
+ userpassword = userpassword,
+ ownerpassword = ownerpassword,
+ -- visiblefilename = "", -- this prohibits the full filename ending up in the file
+ }
+ codeinjections.setfigurecolorspace(data,figure)
+ codeinjections.setfiguremask(data,figure)
+ if figure then
+ -- new, bonus check (a bogus check in lmtx)
+ if page and page > 1 then
+ local f = scanimage {
+ filename = name,
+ userpassword = userpassword,
+ ownerpassword = ownerpassword,
+ }
+ if f and f.page and f.pages < page then
+ report_inclusion("no page %i in %a, using page 1",page,name)
+ page = 1
+ figure.page = page
+ end
+ end
+ -- till here
+ local f, comment = checkimage(scanimage(figure))
+ if not f then
+ ds.comment = comment
+ ds.found = false
+ ds.error = true
+ end
+ if figure.attr and not f.attr then
+ -- tricky as img doesn't allow it
+ f.attr = figure.attr
+ end
+ if dr.cmyk == v_yes then
+ f.enforcecmyk = true
+ elseif dr.cmyk == v_auto and attributes.colors.model == "cmyk" then
+ f.enforcecmyk = true
+ end
+ figure = f
+ end
+ local f, d = codeinjections.setfigurealternative(data,figure)
+ figure = f or figure
+ data = d or data
+ figures_loaded[hash] = figure
+ if trace_conversion then
+ report_inclusion("new graphic, using hash %a",hash)
+ end
+ else
+ if trace_conversion then
+ report_inclusion("existing graphic, using hash %a",hash)
+ end
+ end
+ if figure then
+ local width, height = checktransform(figure,dr.transform)
+ --
+ du.width = width
+ du.height = height
+ du.pages = figure.pages
+ du.depth = figure.depth or 0
+ du.colordepth = figure.colordepth or 0
+ du.xresolution = figure.xres or 0
+ du.yresolution = figure.yres or 0
+ du.xsize = figure.xsize or 0
+ du.ysize = figure.ysize or 0
+ du.rotation = figure.rotation or 0 -- in pdf multiples or 90% in jpeg 1
+ du.orientation = figure.orientation or 0 -- jpeg 1 2 3 4 (0=unset)
+ ds.private = figure
+ ds.hash = hash
+ end
+ return data
+end
+
+local nofimages = 0
+local pofimages = { }
+
+function figures.getrealpage(index)
+ return pofimages[index] or 0
+end
+
+local function updatepage(specification)
+ local n = specification.n
+ pofimages[n] = pofimages[n] or tex.count.realpageno -- so when reused we register the first one only
+end
+
+function includers.generic(data)
+ local dr, du, ds = data.request, data.used, data.status
+ -- here we set the 'natural dimensions'
+ dr.width = du.width
+ dr.height = du.height
+ local hash = figures.hash(data)
+ local figure = figures_used[hash]
+ -- figures.registerresource {
+ -- filename = du.fullname,
+ -- width = dr.width,
+ -- height = dr.height,
+ -- }
+ if figure == nil then
+ figure = ds.private -- the img object
+ if figure then
+ figure = (dr.copyimage or copyimage)(figure)
+ if figure then
+ figure.width = dr.width or figure.width
+ figure.height = dr.height or figure.height
+ end
+ end
+ figures_used[hash] = figure
+ end
+ if figure then
+ local nr = figures.boxnumber
+ nofimages = nofimages + 1
+ ds.pageindex = nofimages
+ local image = wrapimage(figure)
+ local pager = new_latelua { action = updatepage, n = nofimages }
+ image.next = pager
+ pager.prev = image
+ local box = hpack(image)
+ box.width = figure.width
+ box.height = figure.height
+ box.depth = 0
+ texsetbox(nr,box)
+ ds.objectnumber = figure.objnum
+ -- indexed[figure.index] = figure
+ ctx_relocateexternalfigure()
+ end
+ return data
+end
+
+-- -- -- nongeneric -- -- --
+
+local function checkers_nongeneric(data,command) -- todo: macros and context.*
+ local dr, du, ds = data.request, data.used, data.status
+ local name = du.fullname or "unknown nongeneric"
+ local hash = name
+ if dr.object then
+ -- hm, bugged ... waiting for an xform interface
+ if not objects.data["FIG"][hash] then
+ if type(command) == "function" then
+ command()
+ end
+ ctx_dosetfigureobject("FIG",hash)
+ end
+ ctx_doboxfigureobject("FIG",hash)
+ elseif type(command) == "function" then
+ command()
+ end
+ return data
+end
+
+local function includers_nongeneric(data)
+ return data
+end
+
+checkers.nongeneric = checkers_nongeneric
+includers.nongeneric = includers_nongeneric
+
+-- -- -- mov -- -- --
+
+function checkers.mov(data)
+ local dr, du, ds = data.request, data.used, data.status
+ local width = todimen(dr.width or figures.defaultwidth)
+ local height = todimen(dr.height or figures.defaultheight)
+ local foundname = du.fullname
+ dr.width, dr.height = width, height
+ du.width, du.height, du.foundname = width, height, foundname
+ if trace_inclusion then
+ report_inclusion("including movie %a, width %p, height %p",foundname,width,height)
+ end
+ -- we need to push the node.write in between ... we could make a shared helper for this
+ ctx_startfoundexternalfigure(width .. "sp",height .. "sp")
+ context(function()
+ nodeinjections.insertmovie {
+ width = width,
+ height = height,
+ factor = bpfactor,
+ ["repeat"] = dr["repeat"],
+ controls = dr.controls,
+ preview = dr.preview,
+ label = dr.label,
+ foundname = foundname,
+ }
+ end)
+ ctx_stopfoundexternalfigure()
+ return data
+end
+
+includers.mov = includers.nongeneric
+
+-- -- -- mps -- -- --
+
+internalschemes.mprun = true
+
+-- mprun.foo.1 mprun.6 mprun:foo.2
+
+local ctx_docheckfiguremprun = context.docheckfiguremprun
+local ctx_docheckfiguremps = context.docheckfiguremps
+
+local function internal(askedname)
+ local spec, mprun, mpnum = match(lower(askedname),"mprun([:%.]?)(.-)%.(%d+)")
+ -- mpnum = tonumber(mpnum) or 0 -- can be string or number, fed to context anyway
+ if spec ~= "" then
+ return mprun, mpnum
+ else
+ return "", mpnum
+ end
+end
+
+function existers.mps(askedname)
+ local mprun, mpnum = internal(askedname)
+ if mpnum then
+ return askedname, true, "mps", true
+ else
+ return existers.generic(askedname)
+ end
+end
+
+function checkers.mps(data)
+ local mprun, mpnum = internal(data.used.fullname)
+ if mpnum then
+ return checkers_nongeneric(data,function() ctx_docheckfiguremprun(mprun,mpnum) end)
+ else
+ return checkers_nongeneric(data,function() ctx_docheckfiguremps(data.used.fullname) end)
+ end
+end
+
+includers.mps = includers.nongeneric
+
+-- -- -- tex -- -- --
+
+local ctx_docheckfiguretex = context.docheckfiguretex
+
+function existers.tex(askedname)
+ askedname = resolvers.findfile(askedname)
+ return askedname ~= "" and askedname or false, true, "tex", true
+end
+
+function checkers.tex(data)
+ return checkers_nongeneric(data,function() ctx_docheckfiguretex(data.used.fullname) end)
+end
+
+includers.tex = includers.nongeneric
+
+-- -- -- buffer -- -- --
+
+local ctx_docheckfigurebuffer = context.docheckfigurebuffer
+
+function existers.buffer(askedname)
+ local name = file.nameonly(askedname)
+ local okay = buffers.exists(name)
+ return okay and name, true, "buffer", true -- always quit scanning
+end
+
+function checkers.buffer(data)
+ return checkers_nongeneric(data,function() ctx_docheckfigurebuffer(file.nameonly(data.used.fullname)) end)
+end
+
+includers.buffers = includers.nongeneric
+
+-- -- -- auto -- -- --
+
+function existers.auto(askedname)
+ local name = gsub(askedname, ".auto$", "")
+ local format = figures.guess(name)
+ -- if format then
+ -- report_inclusion("format guess %a for %a",format,name)
+ -- else
+ -- report_inclusion("format guess for %a is not possible",name)
+ -- end
+ return format and name, true, format
+end
+
+checkers.auto = checkers.generic
+includers.auto = includers.generic
+
+-- -- -- cld -- -- --
+
+local ctx_docheckfigurecld = context.docheckfigurecld
+
+function existers.cld(askedname)
+ askedname = resolvers.findfile(askedname)
+ return askedname ~= "" and askedname or false, true, "cld", true
+end
+
+function checkers.cld(data)
+ return checkers_nongeneric(data,function() ctx_docheckfigurecld(data.used.fullname) end)
+end
+
+includers.cld = includers.nongeneric
+
+-- -- -- converters -- -- --
+
+setmetatableindex(converters,"table")
+
+-- We keep this helper because it has been around for a while and therefore it can
+-- be a depedency in an existing workflow.
+
+function programs.makeoptions(options)
+ local to = type(options)
+ return (to == "table" and concat(options," ")) or (to == "string" and options) or ""
+end
+
+function programs.run(binary,argument,variables)
+ local found = nil
+ if type(binary) == "table" then
+ for i=1,#binary do
+ local b = binary[i]
+ found = os.which(b)
+ if found then
+ binary = b
+ break
+ end
+ end
+ if not found then
+ binary = concat(binary, " | ")
+ end
+ elseif binary then
+ found = os.which(match(binary,"[%S]+"))
+ end
+ if type(argument) == "table" then
+ argument = concat(argument," ") -- for old times sake
+ end
+ if not found then
+ report_inclusion("program %a is not installed",binary or "?")
+ elseif not argument or argument == "" then
+ report_inclusion("nothing to run, no arguments for program %a",binary)
+ else
+ -- no need to use the full found filename (found) .. we also don't quote the program
+ -- name any longer as in luatex there is too much messing with these names
+ local command = format([[%s %s]],binary,replacetemplate(longtostring(argument),variables))
+ if trace_conversion or trace_programs then
+ report_inclusion("running command: %s",command)
+ end
+ os.execute(command)
+ end
+end
+
+-- the rest of the code has been moved to grph-con.lua
+
+-- -- -- bases -- -- --
+
+local bases = allocate()
+figures.bases = bases
+
+local bases_list = nil -- index => { basename, fullname, xmlroot }
+local bases_used = nil -- [basename] => { basename, fullname, xmlroot } -- pointer to list
+local bases_found = nil
+local bases_enabled = false
+
+local function reset()
+ bases_list = allocate()
+ bases_used = allocate()
+ bases_found = allocate()
+ bases_enabled = false
+ bases.list = bases_list
+ bases.used = bases_used
+ bases.found = bases_found
+end
+
+reset()
+
+function bases.use(basename)
+ if basename == "reset" then
+ reset()
+ else
+ basename = file.addsuffix(basename,"xml")
+ if not bases_used[basename] then
+ local t = { basename, nil, nil }
+ bases_used[basename] = t
+ bases_list[#bases_list+1] = t
+ if not bases_enabled then
+ bases_enabled = true
+ xml.registerns("rlx","http://www.pragma-ade.com/schemas/rlx") -- we should be able to do this per xml file
+ end
+ if trace_bases then
+ report_inclusion("registering base %a",basename)
+ end
+ end
+ end
+end
+
+implement { name = "usefigurebase", actions = bases.use, arguments = "string" }
+
+local function bases_find(basename,askedlabel)
+ if trace_bases then
+ report_inclusion("checking for %a in base %a",askedlabel,basename)
+ end
+ basename = file.addsuffix(basename,"xml")
+ local t = bases_found[askedlabel]
+ if t == nil then
+ local base = bases_used[basename]
+ local page = 0
+ if base[2] == nil then
+ -- no yet located
+ for i=1,#figure_paths do
+ local path = resolveprefix(figure_paths[i]) -- we resolve (e.g. jobfile:)
+ local xmlfile = path .. "/" .. basename
+ if io.exists(xmlfile) then
+ base[2] = xmlfile
+ base[3] = xml.load(xmlfile)
+ if trace_bases then
+ report_inclusion("base %a loaded",xmlfile)
+ end
+ break
+ end
+ end
+ end
+ t = false
+ if base[2] and base[3] then -- rlx:library
+ for e in xml.collected(base[3],"/(*:library|figurelibrary)/*:figure/*:label") do
+ page = page + 1
+ if xml.text(e) == askedlabel then
+ t = {
+ base = file.replacesuffix(base[2],"pdf"),
+ format = "pdf",
+ name = xml.text(e,"../*:file"), -- to be checked
+ page = page,
+ }
+ bases_found[askedlabel] = t
+ if trace_bases then
+ report_inclusion("figure %a found in base %a",askedlabel,base[2])
+ end
+ return t
+ end
+ end
+ if trace_bases and not t then
+ report_inclusion("figure %a not found in base %a",askedlabel,base[2])
+ end
+ end
+ end
+ return t
+end
+
+-- we can access sequential or by name
+
+local function bases_locate(askedlabel)
+ for i=1,#bases_list do
+ local entry = bases_list[i]
+ local t = bases_find(entry[1],askedlabel,1,true)
+ if t then
+ return t
+ end
+ end
+ return false
+end
+
+function identifiers.base(data)
+ if bases_enabled then
+ local dr, du, ds = data.request, data.used, data.status
+ local fbl = bases_locate(dr.name or dr.label)
+ if fbl then
+ du.page = fbl.page
+ du.format = fbl.format
+ du.fullname = fbl.base
+ ds.fullname = fbl.name
+ ds.format = fbl.format
+ ds.page = fbl.page
+ ds.status = 10
+ end
+ end
+ return data
+end
+
+bases.locate = bases_locate
+bases.find = bases_find
+
+identifiers.list = {
+ identifiers.base,
+ identifiers.default
+}
+
+-- tracing
+
+statistics.register("graphics processing time", function()
+ local nofprocessed = figures.nofprocessed
+ if nofprocessed > 0 then
+ local nofnames, nofbadnames = 0, 0
+ for hash, data in next, figures_found do
+ nofnames = nofnames + 1
+ if data.badname then
+ nofbadnames = nofbadnames + 1
+ end
+ end
+ return format("%s seconds including tex, %s processed images, %s unique asked, %s bad names",
+ statistics.elapsedtime(figures),nofprocessed,nofnames,nofbadnames)
+ else
+ return nil
+ end
+end)
+
+-- helper
+
+function figures.applyratio(width,height,w,h) -- width and height are strings and w and h are numbers
+ if not width or width == "" then
+ if not height or height == "" then
+ return figures.defaultwidth, figures.defaultheight
+ else
+ height = todimen(height)
+ if w and h then
+ return height * w/h, height
+ else
+ return figures.defaultwidth, height
+ end
+ end
+ else
+ width = todimen(width)
+ if not height or height == "" then
+ if w and h then
+ return width, width * h/w
+ else
+ return width, figures.defaultheight
+ end
+ else
+ return width, todimen(height)
+ end
+ end
+end
+
+-- example of simple plugins:
+--
+-- figures.converters.png = {
+-- png = function(oldname,newname,resolution)
+-- local command = string.format('gm convert -depth 1 "%s" "%s"',oldname,newname)
+-- logs.report(string.format("running command %s",command))
+-- os.execute(command)
+-- end,
+-- }
+
+-- local n = "foo.pdf"
+-- local d = figures.getinfo(n)
+-- if d then
+-- for i=1,d.used.pages do
+-- local p = figures.getinfo(n,i)
+-- if p then
+-- local u = p.used
+-- print(u.width,u.height,u.orientation)
+-- end
+-- end
+-- end
+
+function figures.getinfo(name,page)
+ if type(name) == "string" then
+ name = { name = name, page = page }
+ end
+ if name.name then
+ local data = figures.push(name)
+ data = figures.identify(data)
+ if data.status and data.status.status > 0 then
+ data = figures.check(data)
+ end
+ figures.pop()
+ return data
+ end
+end
+
+function figures.getpdfinfo(name,page,metadata)
+ -- not that useful but as we have it for detailed inclusion we can as
+ -- we expose it
+ if type(name) ~= "table" then
+ name = { name = name, page = page, metadata = metadata }
+ end
+ return codeinjections.getinfo(name)
+end
+
+-- interfacing
+
+implement {
+ name = "figure_push",
+ scope = "private",
+ actions = figures.push,
+ arguments = {
+ {
+ { "name" },
+ { "label" },
+ { "page" },
+ { "file" },
+ { "size" },
+ { "object" },
+ { "prefix" },
+ { "cache" },
+ { "format" },
+ { "preset" },
+ { "controls" },
+ { "resources" },
+ { "preview" },
+ { "display" },
+ { "mask" },
+ { "conversion" },
+ { "resolution" },
+ { "color" },
+ { "cmyk" },
+ { "arguments" },
+ { "repeat" },
+ { "transform" },
+ { "compact" },
+ { "width", "dimen" },
+ { "height", "dimen" },
+ { "userpassword" },
+ { "ownerpassword" },
+ }
+ }
+}
+
+-- beware, we get a number passed by default
+
+implement { name = "figure_pop", scope = "private", actions = figures.pop }
+implement { name = "figure_done", scope = "private", actions = figures.done }
+implement { name = "figure_dummy", scope = "private", actions = figures.dummy }
+implement { name = "figure_identify", scope = "private", actions = figures.identify }
+implement { name = "figure_scale", scope = "private", actions = figures.scale }
+implement { name = "figure_check", scope = "private", actions = figures.check }
+implement { name = "figure_include", scope = "private", actions = figures.include }
+
+implement {
+ name = "setfigurelookuporder",
+ actions = figures.setorder,
+ arguments = "string"
+}
+
+implement {
+ name = "figure_reset",
+ scope = "private",
+ arguments = { "integer", "dimen", "dimen" },
+ actions = function(box,width,height)
+ figures.boxnumber = box
+ figures.defaultwidth = width
+ figures.defaultheight = height
+ end
+}
+
+-- require("util-lib-imp-gm")
+--
+-- figures.converters.tif.pdf = function(oldname,newname,resolution)
+-- logs.report("graphics","using gm library to convert %a",oldname)
+-- utilities.graphicmagick.convert {
+-- inputname = oldname,
+-- outputname = newname,
+-- }
+-- end
+--
+-- \externalfigure[t:/sources/hakker1b.tiff]
+
+-- something relatively new:
+
+local registered = { }
+
+local ctx_doexternalfigurerepeat = context.doexternalfigurerepeat
+
+implement {
+ name = "figure_register_page",
+ arguments = "3 strings",
+ actions = function(a,b,c)
+ registered[#registered+1] = { a, b, c }
+ context(#registered)
+ end
+}
+
+implement {
+ name = "figure_nof_registered_pages",
+ actions = function()
+ context(#registered)
+ end
+}
+
+implement {
+ name = "figure_flush_registered_pages",
+ arguments = "string",
+ actions = function(n)
+ local f = registered[tonumber(n)]
+ if f then
+ ctx_doexternalfigurerepeat(f[1],f[2],f[3],n)
+ end
+ end
+}
diff --git a/tex/context/base/mkxl/grph-inc.mkxl b/tex/context/base/mkxl/grph-inc.mkxl
index 4a6626617..99ceea4d3 100644
--- a/tex/context/base/mkxl/grph-inc.mkxl
+++ b/tex/context/base/mkxl/grph-inc.mkxl
@@ -21,12 +21,12 @@
\writestatus{loading}{ConTeXt Graphic Macros / Figure Inclusion}
\registerctxluafile{grph-img}{}
-\registerctxluafile{grph-inc}{}
+\registerctxluafile{grph-inc}{autosuffix}
\registerctxluafile{grph-bmp}{}
\registerctxluafile{grph-chk}{}
\registerctxluafile{grph-con}{}
\registerctxluafile{grph-fil}{}
-\registerctxluafile{grph-mem}{}
+%registerctxluafile{grph-mem}{} % obsolete
\registerctxluafile{grph-u3d}{} % this will become a module
%registerctxluafile{grph-swf}{} % this will become a module
diff --git a/tex/context/base/mkxl/grph-trf.lmt b/tex/context/base/mkxl/grph-trf.lmt
new file mode 100644
index 000000000..f68d6a6a8
--- /dev/null
+++ b/tex/context/base/mkxl/grph-trf.lmt
@@ -0,0 +1,126 @@
+if not modules then modules = { } end modules ['grph-trf'] = {
+ version = 1.001,
+ comment = "companion to grph-trf.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- see grph-trf-todo.lua for older preliminary code for the rest
+
+local sind, cosd, tand, abs = math.sind, math.cosd, math.tand, math.abs
+
+local setdimension = tex.setdimensionvalue
+
+local function analyzerotate(rotation,width,height,depth,total,notfit,obeydepth)
+ --
+ -- print(rotation,width,height,depth,notfit,obeydepth)
+ --
+ local sin = sind(rotation)
+ local cos = cosd(rotation)
+ local abssin = abs(sin)
+ local abscos = abs(cos)
+ local xsize = 0
+ local ysize = 0
+ local xposition = 0
+ local yposition = 0
+ local xoffset = 0
+ local yoffset = 0
+ local newwidth = width
+ local newheight = height
+ local newdepth = depth
+ -- print(sin,cos)
+ if sin > 0 then
+ if cos > 0 then
+ xsize = cos * width + sin * total
+ ysize = sin * width + cos * total
+ yposition = cos * total
+ if notfit then
+ xoffset = - sin * height
+ newwidth = sin * depth + cos * width
+ else
+ newwidth = xsize + xposition - xoffset
+ end
+ if obeydepth then
+ yoffset = cos * depth
+ end
+ newheight = ysize - yoffset
+ newdepth = yoffset
+ -- print("case 1, done")
+ else
+ xsize = abscos * width + sin * total
+ ysize = sin * width + abscos * total
+ xposition = abscos * width
+ if notfit then
+ xoffset = - xsize + sin * depth
+ end
+ if obeydepth then
+ yoffset = abscos * height
+ newwidth = abssin * total + abscos * width + xoffset
+ else
+ newwidth = xsize
+ end
+ newheight = ysize - yoffset
+ newdepth = yoffset
+ -- print("case 2, done")
+ end
+ else
+ if cos < 0 then
+ xsize = abscos * width + abssin * total
+ ysize = abssin * width + abscos * total
+ xposition = xsize
+ yposition = abssin * width
+ if notfit then
+ xoffset = - xsize + abssin * height
+ end
+ if obeydepth then
+ yoffset = ysize + cos * depth
+ end
+ newwidth = notfit and (abssin * height) or xsize
+ newheight = ysize - yoffset
+ newdepth = yoffset
+ -- print("case 3, done")
+ else
+ xsize = cos * width + abssin * total
+ ysize = abssin * width + cos * total
+ xposition = abssin * total
+ yposition = cos * total
+ if notfit then
+ xoffset = - abssin * depth
+ newwidth = xsize + xoffset
+ else
+ newwidth = xsize
+ end
+ if obeydepth then
+ yoffset = cos * depth
+ end
+ newheight = ysize - yoffset + sin * width
+ newdepth = yoffset - sin * width
+ -- print("case 4, done")
+ end
+ end
+ setdimension("d_grph_rotate_x_size", xsize)
+ setdimension("d_grph_rotate_y_size", ysize)
+ setdimension("d_grph_rotate_x_position", xposition)
+ setdimension("d_grph_rotate_y_position", yposition)
+ setdimension("d_grph_rotate_x_offset", xoffset)
+ setdimension("d_grph_rotate_y_offset", yoffset)
+ setdimension("d_grph_rotate_new_width", newwidth)
+ setdimension("d_grph_rotate_new_height", newheight)
+ setdimension("d_grph_rotate_new_depth", newdepth)
+end
+
+interfaces.implement {
+ name = "analyzerotate",
+ actions = analyzerotate,
+ arguments = {
+-- "integer",
+ "number",
+ "dimension",
+ "dimension",
+ "dimension",
+ "dimension",
+ "conditional",
+ "conditional",
+ },
+}
diff --git a/tex/context/base/mkxl/grph-trf.mkxl b/tex/context/base/mkxl/grph-trf.mkxl
index 87e4da7f4..550eb6f96 100644
--- a/tex/context/base/mkxl/grph-trf.mkxl
+++ b/tex/context/base/mkxl/grph-trf.mkxl
@@ -24,7 +24,7 @@
%D We could move the calculations to \LUA\ and clean up this lot anyway. On the
%D other hand, there is some danger of messing up so it has a real low priority.
-\registerctxluafile{grph-trf}{}
+\registerctxluafile{grph-trf}{autosuffix}
% local:
@@ -266,18 +266,18 @@
\glet\finalscaleboxyscale \m_grph_scale_used_y_scale
\grph_scale_calculations_report}
-\setvalue{\??scalegrid\v!yes }{\getnoflines \d_grph_scale_used_y_size\edef\p_height{\the\noflines\lineheight}}
-\setvalue{\??scalegrid\v!height }{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\dimexpr\noflines\lineheight+\strutdepth}}
-\setvalue{\??scalegrid\v!depth }{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\dimexpr\noflines\lineheight-\strutdepth}}
-\setvalue{\??scalegrid\v!halfline}{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\dimexpr\noflines\lineheight+.5\lineheight}}
-\setvalue{\??scalegrid\v!fit }{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\noflines\lineheight}}
-\letvalue{\??scalegrid\empty }\donothing
+\defcsname\??scalegrid\v!yes \endcsname{\getnoflines \d_grph_scale_used_y_size\edef\p_height{\the\noflines\lineheight}}
+\defcsname\??scalegrid\v!height \endcsname{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\dimexpr\noflines\lineheight+\strutdepth}}
+\defcsname\??scalegrid\v!depth \endcsname{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\dimexpr\noflines\lineheight-\strutdepth}}
+\defcsname\??scalegrid\v!halfline\endcsname{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\dimexpr\noflines\lineheight+.5\lineheight}}
+\defcsname\??scalegrid\v!fit \endcsname{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\noflines\lineheight}}
+\letcsname\??scalegrid\empty \endcsname\donothing
\def\grph_scale_check_parameters % resolve self referencing loops
{\ifempty\p_maxwidth \else \edef\p_maxwidth {\the\dimexpr\p_maxwidth }\fi
\ifempty\p_maxheight\else \edef\p_maxheight{\the\dimexpr\p_maxheight }\fi
\ifempty\p_lines \else \edef\p_height {\the\dimexpr\p_lines\lineheight}\fi
- \csname\??scalegrid\scaleparameter\c!grid\endcsname}
+ \begincsname\??scalegrid\scaleparameter\c!grid\endcsname}
\def\grph_scale_by_nature % where ! ! ! ! !
{\ifempty\p_width \else \global\d_grph_scale_used_x_size\p_width \fi
@@ -373,16 +373,11 @@
\global\d_grph_scale_used_y_size\zeropoint
%
\ifempty\p_maxwidth
- \ifempty\p_maxheight
- \else
- \ifdim\d_grph_scale_y_size>\p_maxheight\relax
- \global\d_grph_scale_used_y_size\p_maxheight
- \fi
- \fi
- \else
- \ifdim\d_grph_scale_x_size>\p_maxwidth\relax
- \global\d_grph_scale_used_x_size\p_maxwidth
+ \ifempty\p_maxheight\orelse\ifdim\d_grph_scale_y_size>\p_maxheight\relax
+ \global\d_grph_scale_used_y_size\p_maxheight
\fi
+ \orelse\ifdim\d_grph_scale_x_size>\p_maxwidth\relax
+ \global\d_grph_scale_used_x_size\p_maxwidth
\fi
\fi}
@@ -441,45 +436,35 @@
\def\grph_scale_by_dimension_indeed#1#2#3%
{#1\relax
- \ifempty\p_maxwidth \else
- \ifdim\d_grph_scale_used_x_size>\p_maxwidth\relax
- \global\d_grph_scale_used_x_size\p_maxwidth
- #2\relax
- \fi
+ \ifempty\p_maxwidth\orelse\ifdim\d_grph_scale_used_x_size>\p_maxwidth\relax
+ \global\d_grph_scale_used_x_size\p_maxwidth
+ #2\relax
\fi
- \ifempty\p_maxheight \else
- \ifdim\d_grph_scale_used_y_size>\p_maxheight\relax
- \global\d_grph_scale_used_y_size\p_maxheight
- #3\relax
- \fi
+ \ifempty\p_maxheight\orelse\ifdim\d_grph_scale_used_y_size>\p_maxheight\relax
+ \global\d_grph_scale_used_y_size\p_maxheight
+ #3\relax
\fi}
+% we can use \lastnamedcs if we want to squeeze out some more
+
\def\grph_scale_calculate_norm#1#2% todo: swap 1 and 2 and pass one less
{\csname\??scalenorm\ifcsname\??scalenorm#2\endcsname#2\else\s!unknown\fi\endcsname#1#2}
\def\grph_scale_calculate_fact#1%
{\csname\??scalefact\ifcsname\??scalefact#1\endcsname#1\else\s!unknown\fi\endcsname}
-%setvalue{\??scalenorm\v!min }#1#2#3#4#5{\global#1#4}
-\setvalue{\??scalenorm\v!max }#1#2#3#4#5{\global#1#4}
-\setvalue{\??scalenorm\v!fit }#1#2#3#4#5{\global#1#5}
-\setvalue{\??scalenorm\v!broad }#1#2#3#4#5{\global#1\dimexpr#5-4\externalfigureparameter\c!bodyfont\relax}
-\setvalue{\??scalenorm\s!unknown}#1#2#3#4#5{\global#1\dimexpr#2\dimexpr\externalfigureparameter\c!bodyfont/10\relax\relax} % brr ex
-\setvalue{\??scalenorm\v!auto }#1#2#3#4#5{\ifempty#3\else\global#1#3\fi}
-\setvalue{\??scalenorm\empty }#1#2#3#4#5{\ifempty#3\else\global#1#3\fi}
-\setvalue{\??scalenorm\s!default}#1#2#3#4#5{\ifempty#3\else\global#1#3\fi}
-
-\setvalue{\??scalefact\v!min }{\global\settrue \c_grph_scale_swap_factor}
-\setvalue{\??scalefact\s!unknown}{\global\setfalse\c_grph_scale_swap_factor}
-
-% \setvalue{\??scalenorm\v!min }#1#2#3#4#5% an ugly hack
-% {\ifdim\d_grph_scale_used_x_size>\d_grph_scale_h_size
-% \d_grph_scale_used_y_size\vsize
-% \else
-% \d_grph_scale_used_x_size\hsize
-% \fi}
-
-\setvalue{\??scalenorm\v!min}#1#2#3#4#5% an ugly hack
+\defcsname\??scalenorm\v!max \endcsname#1#-#-#2#-{\global#1#2}
+\defcsname\??scalenorm\v!fit \endcsname#1#-#-#-#2{\global#1#2}
+\defcsname\??scalenorm\v!broad \endcsname#1#-#-#-#2{\global#1\dimexpr#2-4\externalfigureparameter\c!bodyfont\relax}
+\defcsname\??scalenorm\s!unknown\endcsname#1#2#-#-#-{\global#1\dimexpr#2\dimexpr\externalfigureparameter\c!bodyfont/10\relax\relax} % brr ex
+\defcsname\??scalenorm\v!auto \endcsname#1#-#2#-#-{\ifempty#2\else\global#1#2\fi}
+\defcsname\??scalenorm\empty \endcsname#1#-#2#-#-{\ifempty#2\else\global#1#2\fi}
+\defcsname\??scalenorm\s!default\endcsname#1#-#2#-#-{\ifempty#2\else\global#1#2\fi}
+
+\defcsname\??scalefact\v!min \endcsname{\global\settrue \c_grph_scale_swap_factor}
+\defcsname\??scalefact\s!unknown\endcsname{\global\setfalse\c_grph_scale_swap_factor}
+
+\defcsname\??scalenorm\v!min\endcsname#-#-#-#-#-% an ugly hack
{\d_grph_scale_used_x_size\hsize
\d_grph_scale_used_y_size\vsize}
@@ -769,14 +754,22 @@
\installcorenamespace {rotatelocation}
\installcorenamespace {rotatepreset}
-% todo: scratchcounters
+%D These are set at the \LUA\ end (it's a cheaper operation than setting a dimen
+%D register and these are actually kind of constants not used in further
+%D calculations):
-\newdimen\d_grph_rotate_x_size
-\newdimen\d_grph_rotate_y_size
-\newdimen\d_grph_rotate_x_offset
-\newdimen\d_grph_rotate_y_offset
-\newdimen\d_grph_rotate_x_position
-\newdimen\d_grph_rotate_y_position
+\mutable\dimendef\d_grph_rotate_x_size \zeropoint
+\mutable\dimendef\d_grph_rotate_y_size \zeropoint
+\mutable\dimendef\d_grph_rotate_x_offset \zeropoint
+\mutable\dimendef\d_grph_rotate_y_offset \zeropoint
+\mutable\dimendef\d_grph_rotate_x_position\zeropoint
+\mutable\dimendef\d_grph_rotate_y_position\zeropoint
+
+\mutable\dimendef\d_grph_rotate_new_width \zeropoint
+\mutable\dimendef\d_grph_rotate_new_height\zeropoint
+\mutable\dimendef\d_grph_rotate_new_depth \zeropoint
+
+%D These aren't:
\newdimen\d_grph_rotate_used_height
@@ -788,10 +781,6 @@
\newdimen\d_grph_rotate_saved_height
\newdimen\d_grph_rotate_saved_depth
-\newdimen\d_grph_rotate_new_width
-\newdimen\d_grph_rotate_new_height
-\newdimen\d_grph_rotate_new_depth
-
\newconditional\c_grph_rotate_obey_depth
\newconditional\c_grph_rotate_not_fit
\newconditional\c_grph_rotate_center
@@ -816,9 +805,11 @@
\fi
\edef\p_rotation_location{\rotateparameter\c!location}%
\edef\p_rotation_rotation{\rotateparameter\c!rotation}%
- \csname\??rotatelocation
- \ifcsname\??rotatelocation\p_rotation_location\endcsname\p_rotation_location\else\v!default\fi
- \endcsname}
+ \ifcsname\??rotatelocation\p_rotation_location\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\grph_rotate_default
+ \fi}
\def\grph_rotate_framed
{\resetrotateparameter\c!location
@@ -832,42 +823,44 @@
{\grph_rotate_finish_indeed
\egroup}
-\setvalue{\??rotatelocation\v!depth}%
+\def\grph_rotate_default
+ {\setfalse\c_grph_rotate_not_fit
+ \setfalse\c_grph_rotate_center
+ \settrue \c_grph_rotate_obey_depth
+ \grph_rotate_framed}
+
+\letcsname\??rotatelocation\v!default\endcsname\grph_rotate_default
+
+\defcsname\??rotatelocation\v!depth\endcsname
{\setfalse\c_grph_rotate_not_fit
\setfalse\c_grph_rotate_center
\settrue \c_grph_rotate_obey_depth
\grph_rotate_normal}
-\setvalue{\??rotatelocation\v!fit}%
+\defcsname\??rotatelocation\v!fit\endcsname
{\settrue \c_grph_rotate_not_fit
\setfalse\c_grph_rotate_center
\settrue \c_grph_rotate_obey_depth
\grph_rotate_normal}
-\setvalue{\??rotatelocation\v!broad}%
+\defcsname\??rotatelocation\v!broad\endcsname
{\setfalse\c_grph_rotate_not_fit
\setfalse\c_grph_rotate_center
\setfalse\c_grph_rotate_obey_depth
\grph_rotate_normal}
-\setvalue{\??rotatelocation\v!high}%
+\defcsname\??rotatelocation\v!high\endcsname
{\setfalse\c_grph_rotate_not_fit
\setfalse\c_grph_rotate_center
\setfalse\c_grph_rotate_obey_depth
\grph_rotate_framed}
-\setvalue{\??rotatelocation\v!middle}%
+\defcsname\??rotatelocation\v!middle\endcsname
{\setfalse\c_grph_rotate_not_fit
\settrue \c_grph_rotate_center
\setfalse\c_grph_rotate_obey_depth % hm, depth ?
\grph_rotate_normal}
-\setvalue{\??rotatelocation\v!default}%
- {\setfalse\c_grph_rotate_not_fit
- \setfalse\c_grph_rotate_center
- \settrue \c_grph_rotate_obey_depth
- \grph_rotate_framed}
-
\permanent\protected\def\dorotatebox#1% {angle} \hbox/\vbox/\vtop % a fast low level one
{\ifcase#1\relax
\expandafter\gobbleoneargument
@@ -901,22 +894,21 @@
\def\grph_rotate_finish_nop
{\boxcursor\box\nextbox}
-\setvalue{\??rotatepreset\v!left}%
+\defcsname\??rotatepreset\v!left\endcsname
{\edef\p_rotation_rotation{90}}
-\setvalue{\??rotatepreset\v!right}%
+\defcsname\??rotatepreset\v!right\endcsname
{\edef\p_rotation_rotation{270}}
-\setvalue{\??rotatepreset\v!inner}%
+\defcsname\??rotatepreset\v!inner\endcsname
{\signalrightpage
\doifelserightpage{\def\p_rotation_rotation{270}}{\def\p_rotation_rotation{90}}}
-\setvalue{\??rotatepreset\v!outer}%
+\defcsname\??rotatepreset\v!outer\endcsname
{\signalrightpage
\doifelserightpage{\def\p_rotation_rotation{90}}{\def\p_rotation_rotation{270}}}
-\setvalue{\??rotatepreset\v!default}%
- {}
+\letcsname\??rotatepreset\v!default\endcsname\empty
\def\grph_rotate_finish_yes
{\begincsname\??rotatepreset\p_rotation_rotation\endcsname
diff --git a/tex/context/base/mkxl/lpdf-col.lmt b/tex/context/base/mkxl/lpdf-col.lmt
index 13f34fe46..778d250c6 100644
--- a/tex/context/base/mkxl/lpdf-col.lmt
+++ b/tex/context/base/mkxl/lpdf-col.lmt
@@ -777,7 +777,7 @@ do
local function startcolor(k)
local m, c = colortoattributes(k)
local t = transparencytoattribute(k)
- if t then
+ if t and t ~= unsetvalue then
return t_cache[t][m][c]
else
return c_cache[m][c]
@@ -793,10 +793,11 @@ do
backends.pdf.tables.vfspecials = vfspecials
vfspecials.startcolor = startcolor
- vfspecials.stopcolor = { "pdf", "text", "Q" }
+ -- vfspecials.stopcolor = { "pdf", "text", "Q" } -- fails
+ vfspecials.stopcolor = { "pdf", "page", "Q" }
vfspecials.startslant = startslant
- vfspecials.stopslant = { "pdf", "text", "Q" }
+ vfspecials.stopslant = { "pdf", "page", "Q" } -- fails
end
diff --git a/tex/context/base/mkxl/lpdf-lmt.lmt b/tex/context/base/mkxl/lpdf-lmt.lmt
index c02a35fe1..28cfff41d 100644
--- a/tex/context/base/mkxl/lpdf-lmt.lmt
+++ b/tex/context/base/mkxl/lpdf-lmt.lmt
@@ -1074,11 +1074,10 @@ local flushrule, flushsimplerule, flushspecialrule, flushimage, flushgroup do
-- place image also used in vf but we can use a different one if we need it
- local imagetypes = images.types -- pdf png jpg jp2 jbig2 stream memstream
+ local imagetypes = images.types -- pdf png jpg jp2 jbig2 stream
local img_none = imagetypes.none
local img_pdf = imagetypes.pdf
local img_stream = imagetypes.stream
- local img_memstream = imagetypes.memstream
local one_bp = 65536 * bpfactor
@@ -1158,7 +1157,7 @@ local flushrule, flushsimplerule, flushspecialrule, flushimage, flushgroup do
-- tricky: xsize and ysize swapped
- if kind == img_pdf or kind == img_stream or kind == img_memstream then
+ if kind == img_pdf or kind == img_stream then
rx, ry, tx, ty = 1/xsize, 1/ysize, xorigin/xsize, yorigin/ysize
else
-- if kind == img_png then
@@ -2621,7 +2620,7 @@ do -- updaters.register("backend.update.pdf",function()
local codeinjections = backends.pdf.codeinjections
- local imagetypes = images.types -- pdf png jpg jp2 jbig2 stream memstream
+ local imagetypes = images.types -- pdf png jpg jp2 jbig2 stream
local img_none = imagetypes.none
local rulecodes = nodes.rulecodes
diff --git a/tex/context/base/mkxl/lxml-ini.mkxl b/tex/context/base/mkxl/lxml-ini.mkxl
index f331718e9..01d0f883f 100644
--- a/tex/context/base/mkxl/lxml-ini.mkxl
+++ b/tex/context/base/mkxl/lxml-ini.mkxl
@@ -47,86 +47,22 @@
\aliased\let\xmlload\xmlloadfile
-% aliased
-
-%let\xmlall \clf_xmlall
-%let\xmlatt \clf_xmlatt
-%let\xmlattdef \clf_xmlattdef
-%let\xmlattribute \clf_xmlattribute
-%let\xmlattributedef \clf_xmlattributedef
-%let\xmlbadinclusions \clf_xmlbadinclusions
-%let\xmlchainatt \clf_xmlchainatt
-%let\xmlchainattdef \clf_xmlchainattdef
-%let\xmlchecknamespace \clf_xmlchecknamespace
-%let\xmlcommand \clf_xmlcommand
-%let\xmlcontext \clf_xmlcontext
-%let\xmlcount \clf_xmlcount
-%let\xmldelete \clf_xmldelete
-%let\xmldirect \clf_xmldirect % in loops, not dt but root
-%let\xmldirectives \clf_xmldirectives
-%let\xmldirectivesafter \clf_xmldirectivesafter
-%let\xmldirectivesbefore \clf_xmldirectivesbefore
-%let\xmldisplayverbatim \clf_xmldisplayverbatim
-%let\xmlelement \clf_xmlelement
-%let\xmlfilter \clf_xmlfilter
-%let\xmlfilterlist \clf_xmlfilterlist
-%let\xmlfirst \clf_xmlfirst
-%let\xmlflush \clf_xmlflush
-%let\xmlflushcontext \clf_xmlflushcontext
-%let\xmlflushlinewise \clf_xmlflushlinewise
-%let\xmlflushpure \clf_xmlflushpure
-%let\xmlflushspacewise \clf_xmlflushspacewise
-%let\xmlflushtext \clf_xmlflushtext
-%let\xmlfunction \clf_xmlfunction
-%let\xmlinclude \clf_xmlinclude
-%let\xmlincludeoptions \clf_xmlincludeoptions
-%let\xmlinclusion \clf_xmlinclusion
-%let\xmlinclusionbase \clf_xmlinclusionbase
-%let\xmlinclusions \clf_xmlinclusions
-%let\xmlindex \clf_xmlindex
-%let\xmlinlineverbatim \clf_xmlinlineverbatim
-%let\xmllast \clf_xmllast
-%let\xmllastatt \clf_xmllastatt
-%let\xmllastmatch \clf_xmllastmatch
-%let\xmllastpar \clf_xmllastpar
-%let\xmlloaddirectives \clf_xmlloaddirectives
-%let\xmlmain \clf_xmlmain
-%let\xmlmatch \clf_xmlmatch
-%let\xmlname \clf_xmlname
-%let\xmlnamespace \clf_xmlnamespace
-%let\xmlnonspace \clf_xmlnonspace
-%let\xmlpar \clf_xmlpar
-%let\xmlparam \clf_xmlparam
-%let\xmlpath \clf_xmlpath
-%let\xmlpopmatch \clf_xmlpopmatch
-%let\xmlpos \clf_xmlpos
-%let\xmlpure \clf_xmlpure
-%let\xmlpushmatch \clf_xmlpushmatch
-%let\xmlraw \clf_xmlraw
-%let\xmlrefatt \clf_xmlrefatt
-%let\xmlregisterns \clf_xmlregisterns % document
-%let\xmlremapname \clf_xmlremapname % element
-%let\xmlremapnamespace \clf_xmlremapnamespace % document
-%let\xmlsave \clf_xmlsave
-%let\xmlsetatt \clf_xmlsetatt
-%let\xmlsetattribute \clf_xmlsetattribute
-%let\xmlsetpar \clf_xmlsetpar
-%let\xmlsetparam \clf_xmlsetparam
-%let\xmlsetsetup \clf_xmlsetsetup
-%let\xmlsnippet \clf_xmlsnippet
-%let\xmlstrip \clf_xmlstrip
-%let\xmlstripanywhere \clf_xmlstripanywhere
-%let\xmlstripnolines \clf_xmlstripnolines
-%let\xmlstripped \clf_xmlstripped
-%let\xmlstrippednolines \clf_xmlstrippednolines
-%let\xmltag \clf_xmltag
-%let\xmltext \clf_xmltext
-%let\xmltobuffer \clf_xmltobuffer % id pattern name
-%let\xmltobuffertextonly \clf_xmltobuffertextonly % id pattern name
-%let\xmltobufferverbose \clf_xmltobufferverbose % id pattern name
-%let\xmltofile \clf_xmltofile % id pattern filename
-%let\xmltoparameters \clf_xmltoparameters
-%let\xmlverbatim \clf_xmlverbatim
+%D These are defined at the \LUA\ end:
+
+% \xmlall \xmlatt \xmlattdef \xmlattribute \xmlattributedef \xmlbadinclusions
+% \xmlchainatt \xmlchainattdef \xmlchecknamespace \xmlcommand \xmlcontext \xmlcount
+% \xmldelete \xmldirect \xmldirectives \xmldirectivesafter \xmldirectivesbefore
+% \xmldisplayverbatim \xmlelement \xmlfilter \xmlfilterlist \xmlfirst \xmlflush
+% \xmlflushcontext \xmlflushlinewise \xmlflushpure \xmlflushspacewise \xmlflushtext
+% \xmlfunction \xmlinclude \xmlincludeoptions \xmlinclusion \xmlinclusionbase
+% \xmlinclusions \xmlindex \xmlinlineverbatim \xmllast \xmllastatt \xmllastmatch
+% \xmllastpar \xmlloaddirectives \xmlmain \xmlmatch \xmlname \xmlnamespace
+% \xmlnonspace \xmlpar \xmlparam \xmlpath \xmlpopmatch \xmlpos \xmlpure
+% \xmlpushmatch \xmlraw \xmlrefatt \xmlregisterns \xmlremapname \xmlremapnamespace
+% \xmlsave \xmlsetatt \xmlsetattribute \xmlsetpar \xmlsetparam \xmlsetsetup
+% \xmlsnippet \xmlstrip \xmlstripanywhere \xmlstripnolines \xmlstripped
+% \xmlstrippednolines \xmltag \xmltext \xmltobuffer \xmltobuffertextonly
+% \xmltobufferverbose \xmltofile \xmltoparameters \xmlverbatim
\aliased\let\xmlposition\xmlindex
@@ -184,25 +120,11 @@
\aliased\let\startxmlraw\clf_xmlstartraw
\aliased\let\stopxmlraw \clf_xmlstopraw
-% these are expandable! todo: \xmldoifelseattribute
+% These are expandable and defined at the \LUA\ end:
-%let\xmldoif \clf_xmldoif
-%let\xmldoifnot \clf_xmldoifnot
-%let\xmldoifelse \clf_xmldoifelse
-%let\xmldoiftext \clf_xmldoiftext
-%let\xmldoifnottext \clf_xmldoifnottext
-%let\xmldoifelsetext \clf_xmldoifelsetext
-
-%let\xmldoifatt \clf_xmldoifatt
-%let\xmldoifnotatt \clf_xmldoifnotatt
-%let\xmldoifelseatt \clf_xmldoifelseatt
-
-%let\xmldoifempty \clf_xmldoifempty
-%let\xmldoifnotempty \clf_xmldoifnotempty
-%let\xmldoifelseempty \clf_xmldoifelseempty
-%let\xmldoifselfempty \clf_xmldoifselfempty
-%let\xmldoifnotselfempty \clf_xmldoifnotselfempty
-%let\xmldoifelseselfempty \clf_xmldoifelseselfempty
+% \xmldoif \xmldoifnot \xmldoifelse \xmldoiftext \xmldoifnottext \xmldoifelsetext
+% \xmldoifatt \xmldoifnotatt \xmldoifelseatt \xmldoifempty \xmldoifnotempty
+% \xmldoifelseempty \xmldoifselfempty \xmldoifnotselfempty \xmldoifelseselfempty
\aliased\let\xmldoiftextelse \xmldoifelsetext
\aliased\let\xmldoifemptyelse \xmldoifelseempty
@@ -519,26 +441,8 @@
\aliased\let\xmlapplyselectors\clf_xmlapplyselectors
-% \let\xmlcatcodes\notcatcodes
+% In the\MKIV\ file there is some commented code wrt handing entities with active
+% characters ... very old and obsolete.
\protect \endinput
-% \newcount\charactersactiveoffset \charactersactiveoffset="10000
-%
-% \startextendcatcodetable\ctxcatcodes
-% \catcode\numexpr\charactersactiveoffset+`<\relax=13
-% \catcode\numexpr\charactersactiveoffset+`&\relax=13
-% \catcode\numexpr\charactersactiveoffset+`>\relax=13
-% \stopextendcatcodetable
-%
-% \startextendcatcodetable\xmlcatcodes % not needed
-% \catcode\numexpr\charactersactiveoffset+`<\relax=13
-% \catcode\numexpr\charactersactiveoffset+`&\relax=13
-% \catcode\numexpr\charactersactiveoffset+`>\relax=13
-% \stopextendcatcodetable
-%
-% \ctxlua { % entities are remembered in the format
-% commands.remapentity("<",characters.activeoffset + utf.byte("<"))
-% commands.remapentity("&",characters.activeoffset + utf.byte("&"))
-% commands.remapentity(">",characters.activeoffset + utf.byte(">"))
-% }
diff --git a/tex/context/base/mkxl/meta-fnt.lmt b/tex/context/base/mkxl/meta-fnt.lmt
index 627dbc746..a0feb0b52 100644
--- a/tex/context/base/mkxl/meta-fnt.lmt
+++ b/tex/context/base/mkxl/meta-fnt.lmt
@@ -6,257 +6,24 @@ if not modules then modules = { } end modules ['meta-fnt'] = {
license = "see context related readme files"
}
-local next = next
-local concat = table.concat
-local format = string.format
-local formatters = string.formatters
-local chardata = characters.data
-local fontdata = fonts.hashes.identifiers
+-- This was one of the demo modules that never was used for real so the \MKIV\
+-- variant has been replaced by one using the newer \LMTX\ interface, but only
+-- because we want to be (sort of) compatible. Instead of doing the work at the
+-- \LUA\ end we do it at the \TEX end.
-local vffonts = fonts.handlers.vf
-
-local mpfonts = fonts.mp or { }
-fonts.mp = mpfonts
-
-mpfonts.version = mpfonts.version or 1.20
-mpfonts.inline = true
-mpfonts.cache = containers.define("fonts", "mp", mpfonts.version, true)
-
-metapost.fonts = metapost.fonts or { }
-
-local function unicodetoactualtext(...)
- unicodetoactualtext = backends.codeinjections.unicodetoactualtext
- return unicodetoactualtext(...)
-end
-
--- a few glocals
-
-local characters, descriptions = { }, { }
-local factor, code, slot, width, height, depth, total, variants, bbox, llx, lly, urx, ury = 100, { }, 0, 0, 0, 0, 0, 0, true, 0, 0, 0, 0
-
-local flusher = {
- startfigure = function(_chr_,_llx_,_lly_,_urx_,_ury_)
- code = { }
- slot = _chr_
- llx = _llx_
- lly = _lly_
- urx = _urx_
- ury = _ury_
- width = urx - llx
- height = ury
- depth = -lly
- total = total + 1
- inline = mpfonts.inline
- end,
- flushfigure = function(t)
- for i=1,#t do
- code[#code+1] = t[i]
- end
- end,
- stopfigure = function()
- local cd = chardata[n]
- local code = unicodetoactualtext(slot,concat(code," ")) or ""
- descriptions[slot] = {
- -- unicode = slot,
- name = cd and cd.adobename,
- width = width * 100,
- height = height * 100,
- depth = depth * 100,
- boundingbox = { llx, lly, urx, ury },
- }
- if inline then
- characters[slot] = {
- commands = {
- { "pdf", "origin", code },
- }
- }
- else
- characters[slot] = {
- commands = {
- {
- "image",
- {
- stream = code,
- bbox = { 0, -depth * 65536, width * 65536, height * 65536 }
- },
- },
- }
- }
- end
- code = nil -- no need to keep that
- end
-}
-
-local function process(mpxformat,name,instances,scalefactor)
- local filename = resolvers.findfile(name)
- local attributes = filename and lfs.isfile(filename) and lfs.attributes(filename)
- if attributes then
- statistics.starttiming(metapost.fonts)
- scalefactor = scalefactor or 1
- instances = instances or metapost.fonts.instances or 1 -- maybe store in liost too
- local fontname = file.removesuffix(file.basename(name))
- local modification = attributes.modification
- local filesize = attributes.size
- local hash = file.robustname(formatters["%s %05i %03i"](fontname,scalefactor*1000,instances))
- local lists = containers.read(mpfonts.cache,hash)
- if not lists or lists.modification ~= modification or lists.filesize ~= filesize or lists.instances ~= instances or lists.scalefactor ~= scalefactor then
- statistics.starttiming(flusher)
- local data = io.loaddata(filename)
- metapost.reset(mpxformat)
- metapost.setoutercolor(2) -- no outer color and no reset either
- lists = { }
- for i=1,instances do
- characters = { }
- descriptions = { }
- metapost.process {
- mpx = mpxformat,
- flusher = flusher,
- askedfig = "all",
- -- incontext = false,
- data = {
- formatters["randomseed := %s ;"](i*10),
- formatters["charscale := %s ;"](scalefactor),
- data,
- },
- }
- lists[i] = {
- characters = characters,
- descriptions = descriptions,
- parameters = {
- designsize = 655360,
- slant = 0,
- space = 333 * scalefactor,
- space_stretch = 166.5 * scalefactor,
- space_shrink = 111 * scalefactor,
- x_height = 431 * scalefactor,
- quad = 1000 * scalefactor,
- extra_space = 0,
- },
- properties = {
- name = formatters["%s-%03i"](hash,i),
- spacer = "space",
- }
- }
- end
- lists.version = metapost.variables.fontversion or "1.000"
- lists.modification = modification
- lists.filesize = filesize
- lists.instances = instances
- lists.scalefactor = scalefactor
- metapost.reset(mpxformat) -- saves memory
- lists = containers.write(mpfonts.cache, hash, lists)
- statistics.stoptiming(flusher)
- end
- variants = variants + #lists
- statistics.stoptiming(metapost.fonts)
- return lists
- else
- return { }
- end
-end
-
-metapost.fonts.flusher = flusher
-metapost.fonts.instances = 1
-metapost.fonts.process = process
-
-local function build(g,v)
- local size = g.specification.size
- local data = process(v[2],v[3],v[4],size/655360,v[6])
- local list = { }
- local t = { }
- for d=1,#data do
- t = fonts.constructors.scale(data[d],-1000)
- -- local id = font.nextid()
- -- t.fonts = { { id = id } }
- fontdata[id] = t
- if v[5] then
- vffonts.helpers.composecharacters(t)
- end
- list[d] = font.define(t)
- end
- for k, v in next, t do -- last t
- g[k] = v -- kind of replace, when not present, make nil
- end
- g.variants = list
-end
-
-vffonts.combiner.commands.metapost = build
-vffonts.combiner.commands.metafont = build
-
-statistics.register("metapost font generation", function()
- if total > 0 then
- local time = statistics.elapsedtime(flusher)
- if total > 0 then
- return format("%i glyphs, %s seconds runtime, %.1f glyphs/second", total, time, total/tonumber(time))
- else
- return format("%i glyphs, %s seconds runtime", total, time)
- end
- end
-end)
-
-statistics.register("metapost font loading",function()
- if variants > 0 then
- local time = statistics.elapsedtime(metapost.fonts)
- if variants > 0 then
- return format("%s seconds, %i instances, %.3f instances/second", time, variants, variants/tonumber(time))
- else
- return format("%s seconds, %i instances", time, variants)
- end
- end
-end)
-
--- fonts.definers.methods.install( "bidi", {
--- {
--- "metapost", -- method
--- "metafun", -- format
--- "fontoeps.mp", -- filename
--- 1, -- instances
--- false, -- compose
--- },
--- } )
-
-local report = logs.reporter("metapost","fonts")
+metapost.fonts = metapost.fonts or { }
function metapost.fonts.define(specification)
- local fontname = specification.fontname or ""
- local filename = specification.filename or ""
- local format = specification.format or "metafun"
- if fontname == "" then
- report("no fontname given")
- return
- end
- if filename == "" then
- report("no filename given for %a",fontname)
- return
- end
- local fullname = resolvers.findfile(filename)
- if fullname == "" then
- report("unable to locate file %a",filename)
- return
- end
- report("generating font %a using format %a and file %a",fontname,format,filename)
- fonts.definers.methods.install(fontname, {
- {
- specification.engine or "metapost",
- format,
- filename,
- specification.instances or 1,
- specification.compose or false,
- },
- } )
+ local fontname = specification.fontname
+ local filename = specification.filename
+ local fontsize = specification.size
+ context.definemetafont (
+ { fontname },
+ { filename },
+ { size and { "at " .. size .. " sp" } or "" }
+ )
end
-interfaces.implement {
- name = "definemetafont",
- actions = metapost.fonts.define,
- arguments = {
- {
- { "fontname" },
- { "filename" },
- }
- }
-}
-
-- metapost.fonts.define {
-- fontname = "bidi",
-- filename = "bidi-symbols.mp",
diff --git a/tex/context/base/mkxl/meta-fnt.mkxl b/tex/context/base/mkxl/meta-fnt.mkxl
index 9efee0ee5..0a2cab991 100644
--- a/tex/context/base/mkxl/meta-fnt.mkxl
+++ b/tex/context/base/mkxl/meta-fnt.mkxl
@@ -17,17 +17,27 @@
\unprotect
-\permanent\tolerant\protected\def\definemetafont[#1]#*[#2]#*[#3]%
- {\clf_definemetafont
- fontname {#1}%
- filename {#2}%
- % no #3 settings yet (compose, instances)
- \relax}
+%D Contrary to the \MKIV\ variant we dont' use the \LUA\ call but the \LUA\ call
+%D uses this here:
-% \startluacode
-% metapost.fonts.define { fontname = "bidi-symbols", filename = "bidi-symbols.mp" }
-% \stopluacode
+\permanent\tolerant\protected\def\definemetafont[#1]#*[#2]#*[#3]%
+ {\doonlyonce{#2}{%
+ \definefontfeature[metafont-#2][metapost=#2]
+ \startMPcalculation{simplefun} input "#2" ; \stopMPcalculation
+ }%
+ \definefont[#1][Mono*metafont-#2 #3]}
-% \definemetafont[bidi-symbols][bidi-symbols.mp]
+%D Demo:
+%D
+%D \starttyping
+%D \setupbodyfont[dejavu]
+%D \starttext
+%D \showglyphs
+%D \startTEXpage[offset=10pt]
+%D \definemetafont[MyDemoA][demo-symbols][at 10pt]\MyDemoA\char9754
+%D \definemetafont[MyDemoB][demo-symbols][at 30pt]\MyDemoB\char9754
+%D \stopTEXpage
+%D \stoptext
+%D \stoptyping
\protect \endinput
diff --git a/tex/context/base/mkxl/mlib-fio.lmt b/tex/context/base/mkxl/mlib-fio.lmt
index 4290537dd..895178b04 100644
--- a/tex/context/base/mkxl/mlib-fio.lmt
+++ b/tex/context/base/mkxl/mlib-fio.lmt
@@ -1,4 +1,4 @@
-if not modules then modules = { } end modules ['mlib-run'] = {
+if not modules then modules = { } end modules ['mlib-fio'] = {
version = 1.001,
comment = "companion to mlib-ctx.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
@@ -80,7 +80,9 @@ local function writetoterminal(terminaldata,maxterm,d)
elseif t == "table" then
for i=1,#d do
local l = d[i]
- if find(l,"[\n\r]") then
+ if not l then
+ -- just ignore
+ elseif find(l,"[\n\r]") then
local s = splitlines(l)
local m = #s
for i=1,m do
@@ -177,10 +179,45 @@ local function finder(name,mode,kind)
return findtexfile(name,kind)
end
+local overloadmode = "warning"
+
+directives.register("metapost.overloadmode",function(v)
+ if v == "warning" or v == "error" then
+ overloadmode = v
+ else
+ overloadmode= false
+ end
+end)
+
+local propertycodes = {
+ [-3] = "mutable",
+ [ 1] = "primitive",
+ [ 2] = "permanent",
+ [ 3] = "immutable",
+}
+
+mplib.propertycodes = propertycodes
+
+local function overload(property,name)
+ if overloadmode then
+ -- turn of warning after format is loaded
+ report_metapost("overloading %s %a",propertycodes[property] or "unknown", name)
+ -- no overload permitted
+ if overloadmode == "error" then
+ luatex.abort()
+ end
+ return false
+ else
+ -- overload permitted
+ return true
+ end
+end
+
function mplib.new(specification)
local openfile = fileopener()
specification.find_file = finder
specification.run_logger = logger
+ specification.run_overload = overload
specification.open_file = openfile
specification.interaction = "silent"
specification.halt_on_error = true
diff --git a/tex/context/base/mkxl/mlib-int.lmt b/tex/context/base/mkxl/mlib-int.lmt
index 86e2e5732..3ea270a79 100644
--- a/tex/context/base/mkxl/mlib-int.lmt
+++ b/tex/context/base/mkxl/mlib-int.lmt
@@ -18,10 +18,11 @@ local exheights = fonts.hashes.exheights
local registerscript = metapost.registerscript
-local on_right_page = structures.pages.on_right
-local is_odd_page = structures.pages.is_odd
-local in_body_page = structures.pages.in_body
-local page_fraction = structures.pages.fraction
+local on_right_page = structures.pages.on_right
+local is_odd_page = structures.pages.is_odd
+local in_body_page = structures.pages.in_body
+local page_fraction = structures.pages.fraction
+local layout_has_changed = structures.pages.has_changed
local function defaultcolormodel() -- can be helper
local colormethod = getcount("MPcolormethod")
@@ -127,9 +128,10 @@ registerscript("OverlayRegion", function() mpstring(getmacro("m_overlay_r
--
-- registerscript("PageFraction", page_fraction)
-registerscript("PageFraction", function() return mpboolean(page_fraction()) end)
-registerscript("OnRightPage", function() return mpboolean(on_right_page()) end)
-registerscript("OnOddPage", function() return mpboolean(is_odd_page ()) end)
-registerscript("InPageBody", function() return mpboolean(in_body_page ()) end)
+registerscript("PageFraction", function() return mpboolean(page_fraction ()) end)
+registerscript("OnRightPage", function() return mpboolean(on_right_page ()) end)
+registerscript("OnOddPage", function() return mpboolean(is_odd_page ()) end)
+registerscript("InPageBody", function() return mpboolean(in_body_page ()) end)
+registerscript("LayoutHasChanged", function() return mpboolean(layout_has_changed()) end)
registerscript("defaultcolormodel", defaultcolormodel)
diff --git a/tex/context/base/mkxl/mlib-lmt.lmt b/tex/context/base/mkxl/mlib-lmt.lmt
index f0b59248a..355674582 100644
--- a/tex/context/base/mkxl/mlib-lmt.lmt
+++ b/tex/context/base/mkxl/mlib-lmt.lmt
@@ -10,15 +10,16 @@ if not modules then modules = { } end modules ['mlib-lmt'] = {
local type = type
-local aux = mp.aux
-local mpdirect = aux.direct
-local mppath = mp.path
+local aux = mp.aux
+local mpdirect = aux.direct
+local mppath = mp.path
-local scan = mp.scan
-local scannumeric = scan.numeric
-local scanpath = scan.path
+local scan = mp.scan
+local scannumeric = scan.numeric
+local scanpath = scan.path
-local getparameter = metapost.getparameter
+local getparameter = metapost.getparameter
+local registerscript = metapost.registerscript
function mp.lmt_function_x(xmin,xmax,xstep,code,shape) -- experimental
local code = "return function(x) return " .. code .. " end"
@@ -126,27 +127,21 @@ function mp.lmt_svg_include()
end
end
-
-function mp.lmt_do_remaptext()
+registerscript("remaptext", function()
local parameters = metapost.scanparameters()
if parameters and parameters.label then
metapost.remaptext(parameters)
end
-end
+end)
do
local dropins = fonts.dropins
- local registerglyphs = dropins.registerglyphs
local registerglyph = dropins.registerglyph
+ local registerglyphs = dropins.registerglyphs
- function mp.lmt_register_glyph()
- registerglyph(metapost.getparameterset("mpsglyph"))
- end
-
- function mp.lmt_register_glyphs()
- registerglyphs(metapost.getparameterset("mpsglyphs"))
- end
+ registerscript("registerglyph", function() registerglyph (metapost.getparameterset("mpsglyph")) end)
+ registerscript("registerglyphs",function() registerglyphs(metapost.getparameterset("mpsglyphs")) end)
end
diff --git a/tex/context/base/mkxl/mlib-pdf.lmt b/tex/context/base/mkxl/mlib-pdf.lmt
index e737b5d86..c1f5045d7 100644
--- a/tex/context/base/mkxl/mlib-pdf.lmt
+++ b/tex/context/base/mkxl/mlib-pdf.lmt
@@ -348,17 +348,17 @@ end
-- lpegmatch(pattern_key,object.prescript,1,variables)
-- end
-function metapost.processspecial(str)
- local code = loadstring(str)
- if code then
- if trace_variables then
- report_metapost("executing special code: %s",str)
- end
- code()
- else
- report_metapost("invalid special code: %s",str)
- end
-end
+-- function metapost.processspecial(str)
+-- local code = loadstring(str)
+-- if code then
+-- if trace_variables then
+-- report_metapost("executing special code: %s",str)
+-- end
+-- code()
+-- else
+-- report_metapost("invalid special code: %s",str)
+-- end
+-- end
local stack = { }
@@ -407,7 +407,7 @@ function metapost.flush(specification,result)
local stopfigure = flusher.stopfigure
local flushfigure = flusher.flushfigure
local textfigure = flusher.textfigure
- local processspecial = flusher.processspecial or metapost.processspecial
+ -- local processspecial = flusher.processspecial or metapost.processspecial
metapost.comment = flusher.comment or nocomment
for index=1,#figures do
local figure = figures[index]
@@ -443,58 +443,7 @@ function metapost.flush(specification,result)
for o=1,#objects do
local object = objects[o]
local objecttype = object.type
- if objecttype == "text" then
- result[#result+1] = "q"
- local ot = object.transform -- 3,4,5,6,1,2
- result[#result+1] = f_cm(ot[3],ot[4],ot[5],ot[6],ot[1],ot[2])
- flushfigure(result) -- flush accumulated literals
- result = { }
- textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth)
- result[#result+1] = "Q"
- elseif objecttype == "special" then
- if processspecial then
- processspecial(object.prescript)
- end
- elseif objecttype == "start_clip" then
- local evenodd = not object.istext and object.postscript == "evenodd"
- result[#result+1] = "q"
- flushnormalpath(object.path,result,false)
- result[#result+1] = evenodd and "W* n" or "W n"
- elseif objecttype == "stop_clip" then
- result[#result+1] = "Q"
- miterlimit, linecap, linejoin, dashed, linewidth = -1, -1, -1, "", false
- elseif objecttype == "start_bounds" or objecttype == "stop_bounds" then
- -- skip
- elseif objecttype == "start_group" then
- if lpdf.flushgroup then
- local before, after = processplugins(object)
- if before then
- result[#result+1] = "q"
- result = pluginactions(before,result,flushfigure)
- insert(groupstack, {
- after = after,
- result = result,
- bbox = toboundingbox(object.path),
- })
- result = { }
- miterlimit, linecap, linejoin, dashed, linewidth = -1, -1, -1, "", false
- else
- insert(groupstack,false)
- end
- else
- insert(groupstack,false)
- end
- elseif objecttype == "stop_group" then
- local data = remove(groupstack)
- if data then
- local reference = lpdf.flushgroup(concat(result,"\r"),data.bbox)
- result = data.result
- result[#result+1] = reference
- result = pluginactions(data.after,result,flushfigure)
- result[#result+1] = "Q"
- miterlimit, linecap, linejoin, dashed, linewidth = -1, -1, -1, "", false
- end
- else
+ if objecttype == "fill" or objecttype == "outline" then
-- we use an indirect table as we want to overload
-- entries but this is not possible in userdata
--
@@ -676,6 +625,60 @@ function metapost.flush(specification,result)
-- can be qQ'd so changes can end up in groups
miterlimit, linecap, linejoin, dashed, linewidth = -1, -1, -1, "", false
end
+ elseif objecttype == "start_clip" then
+ local evenodd = not object.istext and object.postscript == "evenodd"
+ result[#result+1] = "q"
+ flushnormalpath(object.path,result,false)
+ result[#result+1] = evenodd and "W* n" or "W n"
+ elseif objecttype == "stop_clip" then
+ result[#result+1] = "Q"
+ miterlimit, linecap, linejoin, dashed, linewidth = -1, -1, -1, "", false
+ elseif objecttype == "start_bounds" or objecttype == "stop_bounds" then
+ -- skip
+ elseif objecttype == "start_group" then
+ if lpdf.flushgroup then
+ local before, after = processplugins(object)
+ if before then
+ result[#result+1] = "q"
+ result = pluginactions(before,result,flushfigure)
+ insert(groupstack, {
+ after = after,
+ result = result,
+ bbox = toboundingbox(object.path),
+ })
+ result = { }
+ miterlimit, linecap, linejoin, dashed, linewidth = -1, -1, -1, "", false
+ else
+ insert(groupstack,false)
+ end
+ else
+ insert(groupstack,false)
+ end
+ elseif objecttype == "stop_group" then
+ local data = remove(groupstack)
+ if data then
+ local reference = lpdf.flushgroup(concat(result,"\r"),data.bbox)
+ result = data.result
+ result[#result+1] = reference
+ result = pluginactions(data.after,result,flushfigure)
+ result[#result+1] = "Q"
+ miterlimit, linecap, linejoin, dashed, linewidth = -1, -1, -1, "", false
+ end
+ -- if objecttype == "text" then
+ -- result[#result+1] = "q"
+ -- local ot = object.transform -- 3,4,5,6,1,2
+ -- result[#result+1] = f_cm(ot[3],ot[4],ot[5],ot[6],ot[1],ot[2])
+ -- flushfigure(result) -- flush accumulated literals
+ -- result = { }
+ -- textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth)
+ -- result[#result+1] = "Q"
+ -- elseif objecttype == "special" then
+ -- if processspecial then
+ -- processspecial(object.prescript)
+ -- end
+ -- else
+ else
+ -- error
end
end
end
diff --git a/tex/context/base/mkxl/mlib-run.lmt b/tex/context/base/mkxl/mlib-run.lmt
index 602d6f36c..2af504feb 100644
--- a/tex/context/base/mkxl/mlib-run.lmt
+++ b/tex/context/base/mkxl/mlib-run.lmt
@@ -31,7 +31,7 @@ nears zero.</p>
local type, tostring, tonumber, next = type, tostring, tonumber, next
local find, striplines = string.find, utilities.strings.striplines
-local concat, insert, remove = table.concat, table.insert, table.remove
+local concat, insert, remove, sortedkeys = table.concat, table.insert, table.remove, table.sortedkeys
local emptystring = string.is_empty
@@ -641,6 +641,9 @@ do
format = "metafun", -- or: minifun
method = "double",
}
+-- if not code then
+-- code = ""
+-- end
metapost.process {
mpx = mpx,
flusher = flusher,
@@ -661,7 +664,7 @@ do
end
-local getstatistics = mplib.getstatistics or mplib.statistics
+local getstatistics = mplib.getstatistics
function metapost.getstatistics(memonly)
if memonly then
@@ -679,3 +682,26 @@ function metapost.getstatistics(memonly)
return t
end
end
+
+local gethashentries = mplib.gethashentries
+
+function metapost.gethashentries(name,full)
+ if name then
+ local mpx = mpxformats[name] or mpxformats[name .. ":1"]
+ if mpx then
+ return gethashentries(mpx,full)
+ end
+ else
+ local t = { }
+ for name, mpx in next, mpxformats do
+ t[name] = gethashtokens(mpx,full)
+ end
+ return t
+ end
+end
+
+local gethashtokens = mplib.gethashtokens
+
+function metapost.getinstancenames()
+ return sortedkeys(mpxformats)
+end
diff --git a/tex/context/base/mkxl/page-lay.mkxl b/tex/context/base/mkxl/page-lay.mkxl
index 6cd0d9579..01098ae3b 100644
--- a/tex/context/base/mkxl/page-lay.mkxl
+++ b/tex/context/base/mkxl/page-lay.mkxl
@@ -123,6 +123,8 @@
\doifnothing{\directlayoutparameter\c!state}{\letlayoutparameter\c!state\v!start}%
\to \everydefinelayout
+\newconditional \layouthaschanged \settrue\layouthaschanged % used in mp
+
\appendtoks
\ifcase\layoutsetupmode
% can't happen
@@ -153,8 +155,13 @@
\page_layouts_synchronize
\page_layouts_check_next
\fi
+ \global\settrue\layouthaschanged
\to \everysetuplayout
+\appendtoks
+ \global\setfalse\layouthaschanged
+\to \everyaftershipout
+
\permanent\def\doifelselayoutdefined#1%
{\ifcsname\namedlayouthash{#1}\c!state\endcsname % maybe a helper
\expandafter\firstoftwoarguments
diff --git a/tex/context/base/mkxl/syst-lua.lmt b/tex/context/base/mkxl/syst-lua.lmt
index 673c99ae3..faad64ff0 100644
--- a/tex/context/base/mkxl/syst-lua.lmt
+++ b/tex/context/base/mkxl/syst-lua.lmt
@@ -313,10 +313,11 @@ end
do
-- This is some 20% slower than native but we only provide this for compatibility
- -- reasons so we don't care that much about it. Eventually we can drop the
- -- built-in method.
+ -- reasons so we don't care that much about it. Eventually we can drop the built-in
+ -- method.
local channels = { }
+ local lastdone = { }
local findbinfile = resolvers.findbinfile
local loadbinfile = resolvers.loadbinfile
@@ -328,7 +329,20 @@ do
local scancsname = tokens.scanners.csname
local setmacro = tokens.setters.macro
- local vrbcatcodes = tex.vrbcatcodes
+ local rlncatcodes = tex.rlncatcodes
+ local texgetcount = tex.getcount
+
+ local bytes = string.bytes
+ local getcatcode = tex.getcatcode
+
+ local char, concat = string.char, table.concat
+
+ -- This uses the normal bin lookup method that we also use for other files,
+ -- and in principle there is no limit on the amount of files other than the
+ -- operating system imposes.
+
+ local t = { }
+ local l = 0
implement {
name = "openin",
@@ -345,8 +359,10 @@ do
local f = findbinfile(s,"tex")
if f then
channels[n] = opentexfile(f)
+ lastdone[n] = false
else
channels[n] = false
+ lastdone[n] = true
end
end,
}
@@ -362,10 +378,19 @@ do
c:close()
end
channels[n] = false
+ t = { }
end,
}
- implement {
+ -- This is not the fastest routine but hardly used, occasionally for line by line
+ -- input in e.g. tikz which is not that fast anyway so I'll deal with it when it
+ -- really is a bottleneck.
+
+ local readerror = function(what,n)
+ tex.error(string.formatters["too many %s brace tokens in read from channel %i"](what,n))
+ end
+
+ interfaces.implement {
name = "read",
public = true,
usage = "value",
@@ -374,18 +399,67 @@ do
scankeyword("to")
local m = scancsname(true)
local c = channels[n]
- local s = c and c:reader()
- if s then
- s = gsub(s," *$"," ") -- or just "", no need to fake tex here
+ local g = 0
+ local s
+ l = 0
+ if c then
+ while true do
+ local s = c and c:reader()
+ if s then
+ l = l + 1
+ s = gsub(s," *$","")
+ t[l] = s
+ for c in bytes(s) do
+ local cc = getcatcode(c)
+ if cc == 1 then
+ g = g + 1
+ elseif cc == 2 then
+ g = g - 1
+ end
+ end
+ if g <= 0 then
+ break
+ end
+ else
+ break
+ end
+ end
+ if g > 0 then
+ readerror("left",n)
+ s = ""
+ elseif g < 0 then
+ readerror("right",n)
+ s = ""
+ elseif l == 0 then
+ if c:endoffile() then
+ lastdone[n] = true
+ s = "\\par"
+ else
+ s = ""
+ end
+ channels[n] = false
+ else
+ local e = texgetcount("endlinechar") -- we can have tex.endline if needed
+ if e < 0 or e > 127 then
+ e = ""
+ else
+ e = " "
+ l = l + 1
+ t[l] = ""
+ end
+ s = concat(t, e, 1, l)
+ end
else
- channels[n] = false
- -- s = "\\par"
- s = "" -- no need to fake tex here
+ s = ""
end
setmacro(m, s, prefix) -- checks for frozen
end,
}
+ -- This is an etex extension. All characters become catcode 12 but a space gets code
+ -- 10. The etex manual specifies a lineending of catcode 12 too. Should we strip spaces
+ -- at the end? A bit weird command that we never use anyway.
+
implement {
name = "readline",
public = true,
@@ -397,15 +471,21 @@ do
local c = channels[n]
local s = c and c:reader()
if s then
- -- should we strip spaces at the end?
+ local e = texgetcount("endlinechar") -- we can have tex.endline if needed
+ if e > 0 then -- forget about 0
+ s = s .. char(e)
+ end
else
channels[n] = false
s = ""
end
- setmacro(vrbcatcodes, m, s, prefix) -- checks for frozen
+ setmacro(rlncatcodes, m, s, prefix) -- checks for frozen
end,
}
+ -- This one uses the special lua condition option which is kind of experimental
+ -- but seems to work fine.
+
local boolean_value = tokens.values.boolean
implement {
@@ -418,7 +498,8 @@ do
end,
}
- -- for the moment here:
+ -- This one doesn't belong here and it might become a real primitive if we need it
+ -- frequently. So, for the moment we keep it in this file.
local getnest = tex.getnest
diff --git a/tex/context/base/mkxl/trac-inf.lmt b/tex/context/base/mkxl/trac-inf.lmt
index 23000b7d0..301a56e9b 100644
--- a/tex/context/base/mkxl/trac-inf.lmt
+++ b/tex/context/base/mkxl/trac-inf.lmt
@@ -179,9 +179,10 @@ function statistics.show()
end)
register("tex properties", function()
local t = status.gethashstate()
+ local l = status.getlookupstate()
local m = status.gettexstate()
- return format("%s hash slots used of %s, approximate memory usage: %i MB",
- t.top, t.max, m.approximate // (1024 * 1024))
+ return format("%s hash slots used of %s, %s control sequences, approximate memory usage: %i MB",
+ t.top, t.max, l.ptr, m.approximate // (1024 * 1024))
end)
register("callbacks", statistics.callbacks)
-- so far
diff --git a/tex/context/modules/mkiv/m-tikz.mkiv b/tex/context/modules/mkiv/m-tikz.mkiv
index 73124716b..e04d53156 100644
--- a/tex/context/modules/mkiv/m-tikz.mkiv
+++ b/tex/context/modules/mkiv/m-tikz.mkiv
@@ -25,8 +25,12 @@
\popcatcodetable
+\protected\def\tikzerrormessage#1#2#3%
+ {\writestatus{#1}{#2}}
+
\protected\def\starttikzpicture
{\begingroup
+ \ifdefined\PackageError\else \let\PackageError\tikzerrormessage \fi
\overloadmode\zerocount
\tikzpicture}
@@ -34,5 +38,4 @@
{\endtikzpicture
\endgroup}
-
\stopmodule
diff --git a/tex/context/modules/mkiv/s-system-macros.mkxl b/tex/context/modules/mkiv/s-system-macros.mkxl
index 6dcccd048..63ac17913 100644
--- a/tex/context/modules/mkiv/s-system-macros.mkxl
+++ b/tex/context/modules/mkiv/s-system-macros.mkxl
@@ -15,8 +15,17 @@
%D to the more robust \LMTX.
% todo: \permanent\protected\let\select\directselect : same value (add ref)
-
-% todo: when mutable, remove immutable
+%
+% todo: when mutable, remove immutable, decide on how immutable it really is
+%
+% todo: just for the fun of it we can compare the table generated here to mkiv but it
+% needs the same hash exposure as we use in luametatex so i'll do that after all lmtx
+% macros are checked (maybe i need to backport some more but it has a low priority)
+%
+% todo: do we need something similar for lua functions etc (problem: performance hit)
+%
+% todo: and how about metapost ... i need a mp.hashtokens in order to do that and I also
+% need to think about protection there (tricky as we use an avl hash there)
\startmodule[system-macros]
@@ -106,7 +115,7 @@
local permanent = v.permanent and "permanent"
local primitive = v.primitive and "primitive"
local instance = v.instance and "instance"
- local dealtwith = mutable or immutable or frozen or permanent or primitive -- beware: we can have combinations
+ local dealtwith = mutable or immutable or mutable or frozen or permanent or primitive -- beware: we can have combinations
local whatever = find(k,"^[a-z][a-z][a-z]+_")
local cscommand = gsub(v.cmdname or "","_"," ")
local undefined = cscommand == "undefined cs"
@@ -146,14 +155,17 @@ end
context.stoptabulate()
end
- show(true, function(k) return find(k,"^[a-zA-Z]+$") end)
-
- context.page()
+ context.starttitle { title = "Public \\TEX\\ commands" }
+ show(true, function(k) return find(k,"^[a-zA-Z]+$") end)
+ context.stoptitle()
- show(false, function(k) return not find(k,"^[a-zA-Z]+$") and not find(k,crap) end)
+ context.starttitle { title = "Private \\TEX\\ commands" }
+ show(false, function(k) return not find(k,"^[a-zA-Z]+$") and not find(k,crap) end)
+ context.stoptitle()
- tokens.setters.macro("NumberOfVisible", total)
+ tokens.setters.macro("NumberOfVisible", total)
tokens.setters.macro("NumberOfFlagged", flagged)
+
\stopluacode
\page
@@ -175,9 +187,97 @@ the core of \CONTEXT. Noaligned macros are special and dealt deep down when
scanning for alignment specific primitives.
\par \stop
-\starttabulate
-\BC number of visible macros \NC \NumberOfVisible \NC \NR
-\BC number of flagged macros \NC \NumberOfFlagged \NC \NR
+\starttabulate[||r|]
+\BC number of visible macros \NC \NumberOfVisible \NC \NR
+\BC number of flagged macros \NC \NumberOfFlagged \NC \NR
+\stoptabulate
+
+\page
+
+% \startMPcalculation
+% % force a format
+% \stopMPcalculation
+
+\startluacode
+
+ local context = context
+ -- local ctx_NC = context.NC
+ -- local ctx_NR = context.NR
+ -- local ctx_bold = context.bold
+
+ local find = string.find
+ local gsub = string.gsub
+ local create = token.create
+
+ context.starttitle { title = "\\METAFUN\\ commands" }
+
+ metapost.simple("prestine")
+
+ local mptotal = 0
+ local codes = metapost.codes
+ local types = metapost.types
+ local procodes = mplib.propertycodes
+
+ context.startcolumns { n = 5, distance = "1em" }
+ context.nohyphens(false)
+ context.obeyspaces(false)
+ local t = metapost.gethashentries("prestine",true) -- prestine:1
+ if t then
+ table.sort(t,function(a,b) return a[3] < b[3] end)
+ mptotal = #t
+ for i=1,mptotal do
+ local ti = t[i]
+ local code = codes[ti[1]]
+ local property = procodes[ti[2]]
+ local name = ti[3]
+ local vtype = ti[4]
+ local vardef = vtype and types[vtype] == "unsuffixedmacro"
+ context.bgroup()
+ context("%-15s\\quad",vardef and "(vardef) tag" or code)
+ if property == "primitive" then
+ context.bf(false)
+ context.blue(false)
+ elseif property == "permanent" then
+ context.bf(false)
+ context.lightgreen(false)
+ elseif property == "immutable" then
+ context.bf(false)
+ context.lightcyan(false)
+ elseif property == "mutable" then
+ context.bf(false)
+ context.lightmagenta(false)
+ elseif not find(name,"^%a+_") then
+ -- todo:
+ if vardef then
+ context.bf(false)
+ context.lightred(false)
+ end
+ end
+ context.verbatim(name)
+ context.egroup()
+ context.par()
+ end
+ end
+ context.stopcolumns()
+
+ context.stoptitle()
+
+ tokens.setters.macro("NumberOfMetafun", mptotal)
+\stopluacode
+
+\page
+
+\starttabulate[|c|l|]
+\HL
+\NC \type {-3} \NC \bf \lightmagenta mutable \NC \NR
+\NC \type {1} \NC \bf \blue primitive \NC \NR
+\NC \type {2} \NC \bf \lightgreen permanent \NC \NR
+\NC \type {3} \NC \bf \lightcyan immutable \NC \NR
+\HL
+\stoptabulate
+
+\starttabulate[||r|]
+\BC number of metafun entries \NC \NumberOfMetafun \NC \NR
\stoptabulate
\stoptext
diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua
index b40f2c8f2..256bf1175 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 : 2020-12-10 22:23
+-- merge date : 2020-12-15 10:10
do -- begin closure to overcome local limits and interference