diff options
author | Hans Hagen <pragma@wxs.nl> | 2017-02-23 18:12:36 +0100 |
---|---|---|
committer | Context Git Mirror Bot <phg42.2a@gmail.com> | 2017-02-23 18:12:36 +0100 |
commit | c91d92093b72a73a92c3ae3c641137abe6fb64b9 (patch) | |
tree | bfbdc4359ae391d0d96e577fb544023464427e81 /tex/context/base/mkiv | |
parent | e4223677ac0d23e4888e41efda0d2e6aabbe76bf (diff) | |
download | context-c91d92093b72a73a92c3ae3c641137abe6fb64b9.tar.gz |
2017-02-23 17:13:00
Diffstat (limited to 'tex/context/base/mkiv')
27 files changed, 876 insertions, 249 deletions
diff --git a/tex/context/base/mkiv/anch-pos.lua b/tex/context/base/mkiv/anch-pos.lua index 6a9612d9e..2ba9e2420 100644 --- a/tex/context/base/mkiv/anch-pos.lua +++ b/tex/context/base/mkiv/anch-pos.lua @@ -90,8 +90,6 @@ local jobpositions = { job.positions = jobpositions -_plib_ = jobpositions -- might go - local default = { -- not r and paragraphs etc __index = { x = 0, -- x position baseline diff --git a/tex/context/base/mkiv/char-fio.lua b/tex/context/base/mkiv/char-fio.lua index ab2555935..fa69d9356 100644 --- a/tex/context/base/mkiv/char-fio.lua +++ b/tex/context/base/mkiv/char-fio.lua @@ -42,18 +42,19 @@ local reporting = "no" -- per line by default local enforced = { - ["characters.filters.utf.reorder"] = true, ["characters.filters.utf.collapse"] = true, ["characters.filters.utf.decompose"] = true, + ["characters.filters.utf.reorder"] = false, } function utffilters.enable() + -- only used one time (normally) for k, v in next, enforced do if v then if reporting == "yes" then report("%a enabled",k) end - enableaction(textfileactions,v) + enableaction(textfileactions,k) else if reporting == "yes" then report("%a not enabled",k) diff --git a/tex/context/base/mkiv/char-utf.lua b/tex/context/base/mkiv/char-utf.lua index ece9d7a76..f4a6d50e1 100644 --- a/tex/context/base/mkiv/char-utf.lua +++ b/tex/context/base/mkiv/char-utf.lua @@ -409,8 +409,8 @@ function utffilters.addgrapheme(result,first,second) -- can be U+ 0x string or u graphemes[first][second] = result end local pair = first .. second - if not composed[pair] then - composed[pair] = result + if not collapsed[pair] then + collapsed[pair] = result p_composed = nil end end @@ -498,7 +498,8 @@ local function prepare() for k, v in sortedhash(characters.data) do local combining = v.combining -- v.ordering or v.combining if combining then - hash[utfchar(k)] = { utfchar(k), combining, 0 } -- slot 3 can be used in sort + local u = utfchar(k) + hash[u] = { u, combining, 0 } -- slot 3 can be used in sort end end local e = utfchartabletopattern(exceptions) @@ -522,7 +523,7 @@ end -- local collapse = utffilters.collapse -- local decompose = utffilters.decompose --- local preprocess = utffilters.preprocess +-- local reorder = utffilters.reorder -- -- local c1, c2, c3 = "a", "̂", "̃" -- local r2, r3 = "â", "ẫ" @@ -540,7 +541,7 @@ end -- for i=1,10000 do -- collapse(data) -- decompose(data) --- -- preprocess(data) +-- -- reorder(data) -- end -- print(os.clock()-t,decompose(collapse(data))==okay,decompose(collapse(str))) -- end diff --git a/tex/context/base/mkiv/char-utf.mkiv b/tex/context/base/mkiv/char-utf.mkiv index fe9f402ef..3b77771a7 100644 --- a/tex/context/base/mkiv/char-utf.mkiv +++ b/tex/context/base/mkiv/char-utf.mkiv @@ -31,9 +31,9 @@ %D since the source files are rather simple, we postpone the %D initialization till runtime. -\appendtoks - \clf_enableutf % not needed when we create a format so we do it now -\to \everyjob +% \appendtoks +% \clf_enableutf % not needed when we create a format so we do it now +% \to \everyjob %D The next one influences input parsing. %D diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv index 4c044c330..c67cf1c25 100644 --- a/tex/context/base/mkiv/cont-new.mkiv +++ b/tex/context/base/mkiv/cont-new.mkiv @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2017.02.20 17:55} +\newcontextversion{2017.02.23 17:07} %D This file is loaded at runtime, thereby providing an excellent place for %D hacks, patches, extensions and new features. diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv index 31d79390a..dc66b5564 100644 --- a/tex/context/base/mkiv/context.mkiv +++ b/tex/context/base/mkiv/context.mkiv @@ -39,7 +39,7 @@ %D up and the dependencies are more consistent. \edef\contextformat {\jobname} -\edef\contextversion{2017.02.20 17:55} +\edef\contextversion{2017.02.23 17:07} \edef\contextkind {beta} %D For those who want to use this: diff --git a/tex/context/base/mkiv/data-ini.lua b/tex/context/base/mkiv/data-ini.lua index 5ed2cce26..09357368c 100644 --- a/tex/context/base/mkiv/data-ini.lua +++ b/tex/context/base/mkiv/data-ini.lua @@ -217,11 +217,11 @@ end environment.texroot = file.collapsepath(texroot) -if type(profiler) == "table" and not jit then - directives.register("system.profile",function() - profiler.start("luatex-profile.log") - end) -end +-- if type(profiler) == "table" and not jit then +-- directives.register("system.profile",function() +-- profiler.start("luatex-profile.log") +-- end) +-- end -- a forward definition diff --git a/tex/context/base/mkiv/font-dsp.lua b/tex/context/base/mkiv/font-dsp.lua index 2d52c23a9..a5a1ae7c0 100644 --- a/tex/context/base/mkiv/font-dsp.lua +++ b/tex/context/base/mkiv/font-dsp.lua @@ -72,6 +72,7 @@ local streamreader = readers.streamreader local setposition = streamreader.setposition local getposition = streamreader.getposition local skipshort = streamreader.skipshort +local skipbytes = streamreader.skip local readushort = streamreader.readcardinal2 -- 16-bit unsigned integer local readulong = streamreader.readcardinal4 -- 24-bit unsigned integer local readshort = streamreader.readinteger2 -- 16-bit signed integer @@ -79,6 +80,9 @@ local readfword = readshort local readstring = streamreader.readstring local readtag = streamreader.readtag local readbytes = streamreader.readbytes +local readfixed = streamreader.readfixed4 +local read2dot14 = streamreader.read2dot14 +local readinteger = streamreader.readinteger1 local gsubhandlers = { } local gposhandlers = { } @@ -2434,3 +2438,213 @@ function readers.svg(f,fontdata,specification) fontdata.hascolor = true end end + +-- This next few are work in progress. I have no fonts that have these tables so it's a +-- gamble. I'll pick up this thread some day (if needed at all). See font-tst.tex for a +-- possible test. Something for a rainy day and a stack of fresh cd's. + +function readers.fvar(f,fontdata,specification) + local datatable = fontdata.tables.fvar + if datatable then + local tableoffset = datatable.offset + setposition(f,tableoffset) + local majorversion = readushort(f) -- 1 + local minorversion = readushort(f) -- 0 + if majorversion ~= 1 and minorversion ~= 0 then + report("table version %a.%a of %a is not supported (yet), maybe font %s is bad", + majorversion,minorversion,"fvar",fontdata.filename) + return + end + local offsettoaxis = tableoffset + readushort(f) + local nofsizepairs = readushort(f) -- 2 count/size pairs + -- pair 1 + local nofaxis = readushort(f) + local sizeofaxis = readushort(f) + -- pair 2 + local nofinstances = readushort(f) + local sizeofinstances = readushort(f) + -- + local extras = fontdata.extras + local axis = { } + local instances = { } + -- + setposition(f,offsettoaxis) + -- + local function readtuple(f) + local t = { } + for i=1,nofaxis do + t[i] = readfixed(f) + end + return t + end + -- + for i=1,nofaxis do + axis[i] = { + tag = readtag(f), -- ital opsz slnt wdth wght + minimum = readfixed(f), -- we get weird values from a test font ... to be checked + default = readfixed(f), -- idem + maximum = readfixed(f), -- idem + flags = readushort(f), + nameid = extras[readushort(f)], + } + local n = sizeofaxis - 20 + if n > 0 then + skipbytes(f,n) + elseif n < 0 then + -- error + end + end + -- + for i=1,nofinstances do + local subfamid = readushort(f) + local flags = readushort(f) + local tuple = readtuple(f) + local psnameid = false + local nofbytes = 2 + 2 + #tuple * 2 + if nofbytes < sizeofinstances then + psnameid = readushort(f) + nofbytes = nofbytes + 2 + end + instances[i] = { + subfamily = extras[subfamid], + flags = flags, + tuple = tuple, + psname = extras[psnameid] or nil, + } + if nofbytes > 0 then + skipbytes(f,nofbytes) + end + end + -- + fontdata.variable = { + axis = axis, + instances = instances, + } + end +end + +-- function readers.gvar(f,fontdata,specification) +-- end + +-- function readers.hvar(f,fontdata,specification) +-- end + +-- function readers.vvar(f,fontdata,specification) +-- end + +-- We don't need all these dimensions so we only mention those that make +-- sense. Mapping to some function makes most sense. + +local tags = { + hasc = "", -- horizontal ascender OS/2.sTypoAscender + hdsc = "", -- horizontal descender OS/2.sTypoDescender + -- hlgp = "", -- horizontal line gap OS/2.sTypoLineGap + -- hcla = "", -- horizontal clipping ascent OS/2.usWinAscent + -- hcld = "", -- horizontal clipping descent OS/2.usWinDescent + vasc = "", -- vertical ascender vhea.ascent + vdsc = "", -- vertical descender vhea.descent + vlgp = "", -- vertical line gap vhea.lineGap + xhgt = "", -- x height OS/2.sxHeight + cpht = "", -- cap height OS/2.sCapHeight + -- sbxs = "", -- subscript em x size OS/2.ySubscriptXSize + -- sbys = "", -- subscript em y size OS/2.ySubscriptYSize + -- sbxo = "", -- subscript em x offset OS/2.ySubscriptXOffset + -- sbyo = "", -- subscript em y offset OS/2.ySubscriptYOffset + -- spxs = "", -- superscript em x size OS/2.ySuperscriptXSize + -- spys = "", -- superscript em y size OS/2.ySuperscriptYSize + -- spxo = "", -- superscript em x offset OS/2.ySuperscriptXOffset + -- spyo = "", -- superscript em y offset OS/2.ySuperscriptYOffset + -- strs = "", -- strikeout size OS/2.yStrikeoutSize + -- stro = "", -- strikeout offset OS/2.yStrikeoutPosition + -- unds = "", -- underline size post.underlineThickness + -- undo = "", -- underline offset post.underlinePosition +} + +function readers.mvar(f,fontdata,specification) + local datatable = fontdata.tables.mvar + if datatable then + local tableoffset = datatable.offset + setposition(f,tableoffset) + local majorversion = readushort(f) -- 1 + local minorversion = readushort(f) -- 0 + if majorversion ~= 1 and minorversion ~= 0 then + report("table version %a.%a of %a is not supported (yet), maybe font %s is bad", + majorversion,minorversion,"fvar",fontdata.filename) + return + end + -- + local nofaxis = readushort(f) + local recordsize = readushort(f) + local nofrecords = readushort(f) + local offsettostore = tableoffset + readushort(f) + local records = { } + local dimensions = { } + local store = { } + local regions = { } + -- + for i=1,nofrecords do + local tag = readtag(f) + if tags[tag] then + dimensions[tag] = { + outer = readushort(f), + inner = readushort(f), + } + else + skipshort(f,2) + end + end + -- + setposition(f,offsettostore) + local nofaxis = readushort(f) + local nofregions = readushort(f) + for i=1,nofregions do + local t = { } + for i=1,nofaxis do + t[i] = { + start = read2dot14(f), + peak = read2dot14(f), + stop = read2dot14(f), + } + end + regions[i] = t + end + -- + local format = readushort(f) -- 1 + local offset = offsettostore + readulong(f) + local nofdata = readushort(f) + local data = { } + for i=1,nofdata do + data[i] = readulong(f) + offset + end + for i=1,nofdata do + local offset = data[i] + setposition(f,offset) + local nofdeltas = readushort(f) + local nofshort = readushort(f) + local nofregions = readushort(f) + local deltas = { } + local regions = { } + local length = nofshort + nofregions + for i=1,nofregions do + regions[i] = readushort(f) + end + for i=1,nofdeltas do + local t = { } + for i=1,nofshort do + t[i] = readushort(f) + end + for i=1,nofregions do + t[nofshort+i] = readinteger(f) + end + deltas[i] = t + end + data[i] = { + regions = regions, + deltas = deltas, + } + end + end +end + +-- function readers.vorg(f,fontdata,specification) +-- end diff --git a/tex/context/base/mkiv/font-lib.mkvi b/tex/context/base/mkiv/font-lib.mkvi index ed7d896c5..316e70019 100644 --- a/tex/context/base/mkiv/font-lib.mkvi +++ b/tex/context/base/mkiv/font-lib.mkvi @@ -97,6 +97,7 @@ \registerctxluafile{font-ctx}{1.001} % after def as it overloads \registerctxluafile{font-ext}{1.001} +\registerctxluafile{font-lig}{1.001} % only for experiments so try to avoid it \registerctxluafile{font-fbk}{1.001} \registerctxluafile{font-aux}{1.001} diff --git a/tex/context/base/mkiv/font-lig.lua b/tex/context/base/mkiv/font-lig.lua new file mode 100644 index 000000000..823be9c3b --- /dev/null +++ b/tex/context/base/mkiv/font-lig.lua @@ -0,0 +1,52 @@ +if not modules then modules = { } end modules ['font-lig'] = { + 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 module is not loaded but generated a file for plain TeX as a substitute +-- for collapsing the input: "luatex-fonts-lig.lua" with "collapse=yes". + +local standalone = not characters + +if standalone then + require("char-def") + require("char-utf") + if characters.initialize then + characters.initialize() + end +end + +local data = { } -- if we ever preload this i'll cache it + +for first, seconds in next, characters.graphemes do + for second, combined in next, seconds do + data[combined] = { first, second } + end +end + +-- data['c'] = { 'a', 'b' } +-- data['d'] = { 'c', 'c' } + +local feature = { + name = "collapse", + type = "ligature", + prepend = true, + dataset = { + { data = data }, + { data = data }, + } +} + +if standalone then + local filename = "luatex-fonts-lig.lua" + local filedata = "-- this file is generated by context\n\n" + .. "fonts.handlers.otf.addfeature " + .. table.serialize(feature,false) + logs.report("fonts","pseudo ligature file %a saved",filename) + io.savedata(filename,filedata) +else + fonts.handlers.otf.addfeature(feature) +end diff --git a/tex/context/base/mkiv/font-otc.lua b/tex/context/base/mkiv/font-otc.lua index a55320903..a0dda593d 100644 --- a/tex/context/base/mkiv/font-otc.lua +++ b/tex/context/base/mkiv/font-otc.lua @@ -195,13 +195,15 @@ local function addfeature(data,feature,specifications) local stepkey = coverup.stepkey local register = coverup.register - local function prepare_substitution(list,featuretype) + local function prepare_substitution(list,featuretype,nocheck) local coverage = { } local cover = coveractions[featuretype] for code, replacement in next, list do local unicode = tounicode(code) local description = descriptions[unicode] - if description then + if not nocheck and not description then + skip = skip + 1 + else if type(replacement) == "table" then replacement = replacement[1] end @@ -212,20 +214,18 @@ local function addfeature(data,feature,specifications) else skip = skip + 1 end - else - skip = skip + 1 end end return coverage end - local function prepare_alternate(list,featuretype) + local function prepare_alternate(list,featuretype,nocheck) local coverage = { } local cover = coveractions[featuretype] for code, replacement in next, list do local unicode = tounicode(code) local description = descriptions[unicode] - if not description then + if not nocheck and not description then skip = skip + 1 elseif type(replacement) == "table" then local r = { } @@ -248,13 +248,13 @@ local function addfeature(data,feature,specifications) return coverage end - local function prepare_multiple(list,featuretype) + local function prepare_multiple(list,featuretype,nocheck) local coverage = { } local cover = coveractions[featuretype] for code, replacement in next, list do local unicode = tounicode(code) local description = descriptions[unicode] - if not description then + if not nocheck and not description then skip = skip + 1 elseif type(replacement) == "table" then local r, n = { }, 0 @@ -281,16 +281,19 @@ local function addfeature(data,feature,specifications) end end end + inspect(coverage) return coverage end - local function prepare_ligature(list,featuretype) + local function prepare_ligature(list,featuretype,nocheck) local coverage = { } local cover = coveractions[featuretype] for code, ligature in next, list do local unicode = tounicode(code) local description = descriptions[unicode] - if description then + if not nocheck and not description then + skip = skip + 1 + else if type(ligature) == "string" then ligature = { lpegmatch(splitter,ligature) } end @@ -311,8 +314,6 @@ local function addfeature(data,feature,specifications) else skip = skip + 1 end - else - skip = skip + 1 end end return coverage @@ -552,6 +553,7 @@ local function addfeature(data,feature,specifications) local askedsteps = specification.steps or specification.subtables or { specification.data } or { } local featuretype = normalized[specification.type or "substitution"] or "substitution" local featureflags = specification.flags or noflags + local nocheck = specification.nocheck local featureorder = specification.order or { feature } local featurechain = (featuretype == "chainsubstitution" or featuretype == "chainposition") and 1 or 0 local nofsteps = 0 @@ -572,13 +574,13 @@ local function addfeature(data,feature,specifications) local coverage = nil local format = nil if featuretype == "substitution" then - coverage = prepare_substitution(list,featuretype) + coverage = prepare_substitution(list,featuretype,nocheck) elseif featuretype == "ligature" then - coverage = prepare_ligature(list,featuretype) + coverage = prepare_ligature(list,featuretype,nocheck) elseif featuretype == "alternate" then - coverage = prepare_alternate(list,featuretype) + coverage = prepare_alternate(list,featuretype,nocheck) elseif featuretype == "multiple" then - coverage = prepare_multiple(list,featuretype) + coverage = prepare_multiple(list,featuretype,nocheck) elseif featuretype == "kern" then format = "kern" coverage = prepare_kern(list,featuretype) @@ -605,16 +607,16 @@ local function addfeature(data,feature,specifications) local format = nil if featuretype == "substitution" then category = "gsub" - coverage = prepare_substitution(list,featuretype) + coverage = prepare_substitution(list,featuretype,nocheck) elseif featuretype == "ligature" then category = "gsub" - coverage = prepare_ligature(list,featuretype) + coverage = prepare_ligature(list,featuretype,nocheck) elseif featuretype == "alternate" then category = "gsub" - coverage = prepare_alternate(list,featuretype) + coverage = prepare_alternate(list,featuretype,nocheck) elseif featuretype == "multiple" then category = "gsub" - coverage = prepare_multiple(list,featuretype) + coverage = prepare_multiple(list,featuretype,nocheck) elseif featuretype == "kern" then category = "gpos" format = "kern" diff --git a/tex/context/base/mkiv/font-otj.lua b/tex/context/base/mkiv/font-otj.lua index fdee3513f..50330cb83 100644 --- a/tex/context/base/mkiv/font-otj.lua +++ b/tex/context/base/mkiv/font-otj.lua @@ -25,7 +25,7 @@ if not modules then modules = { } end modules ['font-otj'] = { if not nodes.properties then return end -local next, rawget = next, rawget +local next, rawget, tonumber = next, rawget, tonumber local fastcopy = table.fastcopy local registertracker = trackers.register diff --git a/tex/context/base/mkiv/font-otr.lua b/tex/context/base/mkiv/font-otr.lua index a9d3a8b29..c5c5d8f32 100644 --- a/tex/context/base/mkiv/font-otr.lua +++ b/tex/context/base/mkiv/font-otr.lua @@ -65,11 +65,9 @@ if not modules then modules = { } end modules ['font-otr'] = { -- require("char-ini") -- end -local next, type, unpack = next, type, unpack -local byte, lower, char, strip, gsub = string.byte, string.lower, string.char, string.strip, string.gsub -local bittest = bit32.btest -local concat, remove, unpack, fastcopy = table.concat, table.remov, table.unpack, table.fastcopy -local floor, abs, sqrt, round = math.floor, math.abs, math.sqrt, math.round +local next, type = next, type +local byte, lower, char, gsub = string.byte, string.lower, string.char, string.gsub +local floor, round = math.floor, math.round local P, R, S, C, Cs, Cc, Ct, Carg, Cmt = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.Carg, lpeg.Cmt local lpegmatch = lpeg.match @@ -120,7 +118,7 @@ local readoffset = readushort local read2dot14 = streamreader.read2dot14 -- 16-bit signed fixed number with the low 14 bits of fraction (2.14) (F2DOT14) function streamreader.readtag(f) - return lower(strip(readstring(f,4))) + return lower(stripstring(readstring(f,4))) end -- date represented in number of seconds since 12:00 midnight, January 1, 1904. The value is represented as a @@ -174,6 +172,7 @@ local reservednames = { [0] = "wwssubfamily", "lightbackgroundpalette", "darkbackgroundpalette", + "variationspostscriptnameprefix", } -- more at: https://www.microsoft.com/typography/otspec/name.htm @@ -747,7 +746,8 @@ function readers.name(f,fontdata,specification) local encoding = encodings[encoding] local language = languages[language] if encoding and language then - local name = reservednames[readushort(f)] + local index = readushort(f) + local name = reservednames[index] if name then namelist[#namelist+1] = { platform = platform, @@ -758,7 +758,14 @@ function readers.name(f,fontdata,specification) offset = start + readushort(f), } else - skipshort(f,2) +namelist[#namelist+1] = { + platform = platform, + encoding = encoding, + language = language, + length = readushort(f), + offset = start + readushort(f), +} +-- skipshort(f,2) end else skipshort(f,3) @@ -783,8 +790,9 @@ function readers.name(f,fontdata,specification) -- -- we need to choose one we like, for instance an unicode one -- - local names = { } - local done = { } + local names = { } + local done = { } + local extras = { } -- -- there is quite some logic in ff ... hard to follow so we start simple -- and extend when we run into it (todo: proper reverse hash) .. we're only @@ -795,7 +803,7 @@ function readers.name(f,fontdata,specification) for i=1,#namelist do local name = namelist[i] local nametag = name.name - if not done[nametag] then + if not done[nametag or i] then local encoding = name.encoding local language = name.language if (not e or encoding == e) and (not l or language == l) then @@ -808,13 +816,16 @@ function readers.name(f,fontdata,specification) if decoder then content = decoder(content) end - names[nametag] = { - content = content, - platform = platform, - encoding = encoding, - language = language, - } - done[nametag] = true + if nametag then + names[nametag] = { + content = content, + platform = platform, + encoding = encoding, + language = language, + } + end + extras[i-1] = content + done[nametag or i] = true end end end @@ -827,7 +838,8 @@ function readers.name(f,fontdata,specification) filter("macintosh") filter("unicode") -- - fontdata.names = names + fontdata.names = names + fontdata.extras = extras -- if specification.platformnames then local collected = { } @@ -1288,7 +1300,8 @@ local sequence = { local supported = { } for i=1,#sequence do - local sp, se, sf = unpack(sequence[i]) + local si = sequence[i] + local sp, se, sf = si[1], si[2], si[3] local p = supported[sp] if not p then p = { } @@ -1704,7 +1717,8 @@ function readers.cmap(f,fontdata,specification) -- local ok = false for i=1,#sequence do - local sp, se, sf = unpack(sequence[i]) + local si = sequence[i] + local sp, se, sf = si[1], si[2], si[3] if checkcmap(f,fontdata,records,sp,se,sf) > 0 then ok = true end @@ -2000,7 +2014,6 @@ local function readdata(f,offset,specification) end end -- - -- readers["os/2"](f,fontdata,specification) readers["head"](f,fontdata,specification) readers["maxp"](f,fontdata,specification) @@ -2023,6 +2036,12 @@ local function readdata(f,offset,specification) readers["gpos"](f,fontdata,specification) readers["math"](f,fontdata,specification) -- + -- readers["fvar"](f,fontdata,specification) + -- readers["hvar"](f,fontdata,specification) + -- readers["vvar"](f,fontdata,specification) + -- readers["mvar"](f,fontdata,specification) + -- readers["vorg"](f,fontdata,specification) + -- fontdata.locations = nil fontdata.tables = nil fontdata.cidmaps = nil diff --git a/tex/context/base/mkiv/font-ots.lua b/tex/context/base/mkiv/font-ots.lua index 22dd082d1..742a0c561 100644 --- a/tex/context/base/mkiv/font-ots.lua +++ b/tex/context/base/mkiv/font-ots.lua @@ -125,7 +125,6 @@ local fonts = fonts local otf = fonts.handlers.otf local tracers = nodes.tracers -local trace_lookups = false registertracker("otf.lookups", function(v) trace_lookups = v end) local trace_singles = false registertracker("otf.singles", function(v) trace_singles = v end) local trace_multiples = false registertracker("otf.multiples", function(v) trace_multiples = v end) local trace_alternatives = false registertracker("otf.alternatives", function(v) trace_alternatives = v end) @@ -738,7 +737,11 @@ end local function get_alternative_glyph(start,alternatives,value) local n = #alternatives - if value == "random" then + if n == 1 then + -- we could actually change that into a gsub and save some memory in the + -- font loader but it makes tracing more messy + return alternatives[1], trace_alternatives and "1 (only one present)" + elseif value == "random" then local r = getrandom and getrandom("glyph",1,n) or random(1,n) return alternatives[r], trace_alternatives and formatters["value %a, taking %a"](value,r) elseif value == "first" then @@ -2356,7 +2359,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) size = l - f + 1 if size > 1 then -- before/current/after | before/current | current/after - local discfound = nil + local discfound -- = nil local n = f + 1 -- last = getnext(last) -- the second in current (first already matched) last = startnext -- the second in current (first already matched) @@ -2485,7 +2488,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) -- sweeptype = nil end if prev then - local discfound = nil + local discfound -- = nil local n = f - 1 while n >= 1 do if prev then @@ -2597,7 +2600,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) end -- maybe only if match prev = getprev(prev) - elseif seq[n][32] and id == glue_code and isspace(prev,threshold,id) then + elseif id == glue_code and seq[n][32] and isspace(prev,threshold,id) then n = n - 1 prev = getprev(prev) else @@ -2619,14 +2622,12 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) -- after if match and s > l then local current = last and getnext(last) - if not current then - if sweeptype == "post" or sweeptype == "replace" then - current = getnext(sweepnode) - -- sweeptype = nil - end + if not current and (sweeptype == "post" or sweeptype == "replace") then + current = getnext(sweepnode) + -- sweeptype = nil end if current then - local discfound = nil + local discfound -- = nil -- removed optimization for s-l == 1, we have to deal with marks anyway local n = l + 1 while n <= s do @@ -2731,7 +2732,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) end -- maybe only if match current = getnext(current) - elseif seq[n][32] and id == glue_code and isspace(current,threshold,id) then + elseif id == glue_code and seq[n][32] and isspace(current,threshold,id) then n = n + 1 current = getnext(current) else @@ -2910,6 +2911,14 @@ do -- overcome local limit end +-- Functions like kernrun, comprun etc evolved over time and in the end look rather +-- complex. It's a bit of a compromis between extensive copying and creating subruns. +-- The logic has been improved a lot by Kai and Ivo who use complex fonts which +-- really helped to identify border cases on the one hand and get insight in the diverse +-- ways fonts implement features (not always that consistent and efficient). At the same +-- time I tried to keep the code relatively efficient so that the overhead in runtime +-- stays acceptable. + local function report_disc(what,n) report_run("%s: %s > %s",what,n,languages.serializediscretionary(n)) end @@ -3014,7 +3023,7 @@ local function kernrun(disc,k_run,font,attr,...) return nextstart, done end --- fonts like ebgaramond do ligatuires this way (less efficient than e.g. dejavu which +-- fonts like ebgaramond do ligatures this way (less efficient than e.g. dejavu which -- will do the testrun variant) local function comprun(disc,c_run,...) -- vararg faster than the whole list @@ -3741,6 +3750,14 @@ otf.helpers = otf.helpers or { } otf.helpers.txtdirstate = txtdirstate otf.helpers.pardirstate = pardirstate +-- This is the main loop. We run over the node list dealing with a specific font. The +-- attribute is a context specific thing. We could work on sub start-stop ranges instead +-- but I wonder if there is that much speed gain (experiments showed that it made not +-- much sense) and we need to keep track of directions anyway. Also at some point I +-- want to play with font interactions and then we do need the full sweeps. Apart from +-- optimizations the principles of processing the features hasn't changed much since +-- the beginning. + local function featuresprocessor(head,font,attr) local sequences = sequencelists[font] -- temp hack @@ -3791,11 +3808,6 @@ local function featuresprocessor(head,font,attr) local dirstack = { } -- could move outside function but we can have local runs sweephead = { } - -- We could work on sub start-stop ranges instead but I wonder if there is that - -- much speed gain (experiments showed that it made not much sense) and we need - -- to keep track of directions anyway. Also at some point I want to play with - -- font interactions and then we do need the full sweeps. - -- Keeping track of the headnode is needed for devanagari. (I generalized it a bit -- so that multiple cases are also covered.) We could prepend a temp node. @@ -3906,6 +3918,9 @@ local function featuresprocessor(head,font,attr) elseif char == false then -- whatever glyph start = getnext(start) + elseif id == glue_code then + -- happens often + start = getnext(start) elseif id == disc_code then local ok if gpossing then @@ -3977,6 +3992,10 @@ local function featuresprocessor(head,font,attr) start = getnext(start) end elseif char == false then + -- whatever glyph + start = getnext(start) + elseif id == glue_code then + -- happens often start = getnext(start) elseif id == disc_code then local ok diff --git a/tex/context/base/mkiv/luat-cnf.lua b/tex/context/base/mkiv/luat-cnf.lua index 9d37df7bb..c26b4775b 100644 --- a/tex/context/base/mkiv/luat-cnf.lua +++ b/tex/context/base/mkiv/luat-cnf.lua @@ -70,9 +70,8 @@ function texconfig.init() "epdf", "fontloader", "kpse", "mplib", }, obsolete = { - "fontforge", -- can be filled by luat-log + "fontloader", -- can be filled by luat-log "kpse", - "token", }, functions = { "assert", "pcall", "xpcall", "error", "collectgarbage", diff --git a/tex/context/base/mkiv/node-fin.lua b/tex/context/base/mkiv/node-fin.lua index 03c8a7607..ffb2ae49e 100644 --- a/tex/context/base/mkiv/node-fin.lua +++ b/tex/context/base/mkiv/node-fin.lua @@ -327,7 +327,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at if id == glyph_code then check = true elseif id == disc_code then - check = true -- notneeded when we flatten replace + check = true -- not needed when we flatten replace elseif id == glue_code then leader = getleader(stack) if leader then diff --git a/tex/context/base/mkiv/publ-imp-apa.lua b/tex/context/base/mkiv/publ-imp-apa.lua index f2e7f11e1..ec46b7f7e 100644 --- a/tex/context/base/mkiv/publ-imp-apa.lua +++ b/tex/context/base/mkiv/publ-imp-apa.lua @@ -191,7 +191,7 @@ categories.inbook = { optional = { "withauthor", "translator", "subtitle", "type", "file", - "booktitle", + "booktitle", "subbooktitle", -- APA ignores this: "chapter", "editionset", "series", "month", @@ -217,6 +217,7 @@ categories.incollection = { optional = { "withauthor", "translator", "subtitle", "type", "file", + "subbooktitle", "editionset", "series", -- APA ignores this: "chapter", "month", diff --git a/tex/context/base/mkiv/publ-imp-apa.mkvi b/tex/context/base/mkiv/publ-imp-apa.mkvi index 6470bb089..77fcb8995 100644 --- a/tex/context/base/mkiv/publ-imp-apa.mkvi +++ b/tex/context/base/mkiv/publ-imp-apa.mkvi @@ -88,6 +88,14 @@ [\c!authorconversion=normalshort] \definebtx + [apa:\s!list:director] + [apa:\s!list:author] + +\definebtx + [apa:\s!list:producer] + [apa:\s!list:author] + +\definebtx [apa:\s!list:suffix] [apa:\s!list] diff --git a/tex/context/base/mkiv/publ-ini.lua b/tex/context/base/mkiv/publ-ini.lua index 86dc6cea2..52642c89d 100644 --- a/tex/context/base/mkiv/publ-ini.lua +++ b/tex/context/base/mkiv/publ-ini.lua @@ -1984,6 +1984,33 @@ do arguments = { "string", "string" } } + local function identical(a,b) + local na, nb = #a, #b + if na ~= nb then + return false + end + if na > 0 then + for i=1,na do + if not identical(a[i],b[i]) then + return false + end + end + return true + end + local ha, hb = a.hash, b.hash + if ha then + return ha == hb + end + for k, v in next, a do + if k == "original" or k == "snippets" then + -- skip diagnostic info + elseif v ~= b[k] then + return false + end + end + return true + end + function lists.sameasprevious(dataset,i,name,order,method) local rendering = renderings[dataset] local list = rendering.list @@ -2030,7 +2057,7 @@ do if c_casted and c_casted == p_casted then sameentry = true elseif type(c_casted) == "table" and type(p_casted) == "table" then - sameentry = table.identical(c_casted,p_casted) + sameentry = identical(c_casted,p_casted) end end if trace_detail then diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf Binary files differindex 1441c0e6c..6d7f14ae0 100644 --- a/tex/context/base/mkiv/status-files.pdf +++ b/tex/context/base/mkiv/status-files.pdf diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf Binary files differindex cc2383ec2..189773237 100644 --- a/tex/context/base/mkiv/status-lua.pdf +++ b/tex/context/base/mkiv/status-lua.pdf diff --git a/tex/context/base/mkiv/trac-deb.lua b/tex/context/base/mkiv/trac-deb.lua index 782be154f..d1168a3a9 100644 --- a/tex/context/base/mkiv/trac-deb.lua +++ b/tex/context/base/mkiv/trac-deb.lua @@ -372,3 +372,15 @@ implement { name = "disableexperiments", actions = experiments.disable, argument implement { name = "showdebuginfo", actions = lmx.showdebuginfo } implement { name = "overloaderror", actions = lmx.overloaderror } implement { name = "showlogcategories", actions = logs.show } + +directives.register("system.profile",function() + luatex.registerstopactions(function() + debugger.disable() + debugger.savestats("luatex-profile.log") + report_nl() + logs.report("system","profiler stopped, log saved in %a","luatex-profile.log") + report_nl() + end) + logs.report("system","profiler started") + debugger.enable() +end) diff --git a/tex/context/base/mkiv/util-deb.lua b/tex/context/base/mkiv/util-deb.lua index ee732b3b5..040a7a9cf 100644 --- a/tex/context/base/mkiv/util-deb.lua +++ b/tex/context/base/mkiv/util-deb.lua @@ -12,87 +12,236 @@ if not modules then modules = { } end modules ['util-deb'] = { local debug = require "debug" -local getinfo = debug.getinfo +local getinfo, sethook = debug.getinfo, debug.sethook local type, next, tostring = type, next, tostring -local format, find = string.format, string.find -local is_boolean = string.is_boolean +local format, find, sub, gsub = string.format, string.find, string.sub, string.gsub +local insert, remove, sort = table.insert, table.remove, table.sort utilities = utilities or { } local debugger = utilities.debugger or { } utilities.debugger = debugger -local counters = { } +local report = logs.reporter("debugger") + +local ticks = os.gettimeofday or os.clock +local seconds = function(n) return n or 0 end +local overhead = 0 +local dummycalls = 10*1000 +local nesting = 0 local names = { } -local report = logs.reporter("debugger") +local function initialize() --- one + if FFISUPPORTED and ffi then -local function hook() - local f = getinfo(2) -- "nS" - if f then - local n = "unknown" - if f.what == "C" then - n = f.name or '<anonymous>' - if not names[n] then - names[n] = format("%42s",n) + if os.type == "windows" then + local okay, kernel = pcall(ffi.load,"kernel32") + if kernel then + local tonumber = ffi.number or tonumber +ffi.cdef[[ +int QueryPerformanceFrequency(int64_t *lpFrequency); +int QueryPerformanceCounter(int64_t *lpPerformanceCount); +]] + local target = ffi.new("__int64[1]") + ticks = function() + if kernel.QueryPerformanceCounter(target) == 1 then + return tonumber(target[0]) + else + return 0 + end + end + local target = ffi.new("__int64[1]") + seconds = function(ticks) + if kernel.QueryPerformanceFrequency(target) == 1 then + return ticks / tonumber(target[0]) + else + return 0 + end + end end - else - -- source short_src linedefined what name namewhat nups func - n = f.name or f.namewhat or f.what - if not n or n == "" then - n = "?" + elseif os.type == "unix" then + local C = ffi.C + local tonumber = ffi.number or tonumber + -- 1 = CLOCK_MONOTONIC + -- 2 = CLOCK_PROCESS_CPUTIME_ID +ffi.cdef [[ +struct timespec { long sec; long nsec; }; +int clock_gettime(int timerid, struct timespec *t); + ]] + local target = ffi.new("timespec[?]",1) + function ticks() + C.clock_gettime(2,target) + return tonumber(target[0].sec*1000000000 + target[0].nsec) end - if not names[n] then - names[n] = format("%42s : % 5i : %s",n,f.linedefined or 0,f.short_src or "unknown source") + seconds = function(ticks) + return ticks/1000000000 end + end - counters[n] = (counters[n] or 0) + 1 end + + initialize = false + end -function debugger.showstats(printer,threshold) -- hm, something has changed, rubish now - printer = printer or report - threshold = threshold or 0 - local total, grandtotal, functions = 0, 0, 0 - local dataset = { } - for name, count in next, counters do - dataset[#dataset+1] = { name, count } +table.setmetatableindex(names,function(t,name) + local v = table.setmetatableindex(function(t,source) + local v = table.setmetatableindex(function(t,line) + local v = { total = 0, count = 0 } + t[line] = v + return v + end) + t[source] = v + return v + end) + t[name] = v + return v +end) + +local function hook(where) + local f = getinfo(2,"nSl") + if f then + local source = f.short_src + if not source then + return + end + local line = f.linedefined or 0 + local name = f.name + if not name then + local what = f.what + if what == "C" then + name = "<anonymous>" + else + name = f.namewhat or what or "<unknown>" + end + end + local data = names[name][source][line] + if where == "call" then + data.count = data.count + 1 + insert(data,ticks()) + elseif where == "return" then + local t = remove(data) + if t then + data.total = data.total + ticks() - t + end + end end - table.sort(dataset,function(a,b) return a[2] == b[2] and b[1] > a[1] or a[2] > b[2] end) - for i=1,#dataset do - local d = dataset[i] - local name = d[1] - local count = d[2] - if count > threshold and not find(name,"for generator") then -- move up - printer(format("%8i %s\n", count, names[name])) - total = total + count +end + +function debugger.showstats(printer) + local printer = printer or report + local calls = 0 + local functions = 0 + local dataset = { } + local length = 0 + local wholetime = 0 + for name, sources in next, names do + for source, lines in next, sources do + for line, data in next, lines do + if #name > length then + length = #name + end + local total = data.total + local count = data.count + local real = total + if real > 0 then + real = total - (count * overhead / dummycalls) + if real < 0 then + real = 0 + end + wholetime = wholetime + real + end + if line < 0 then + line = 0 + end + dataset[#dataset+1] = { real, total, count, name, source, line } + end + end + end + sort(dataset,function(a,b) + if a[1] == b[1] then + if a[2] == b[2] then + if a[3] == b[3] then + if a[4] == b[4] then + if a[5] == b[5] then + return a[6] < b[6] + else + return a[5] < b[5] + end + else + return a[4] < b[4] + end + else + return b[3] < a[3] + end + else + return b[2] < a[2] + end + else + return b[1] < a[1] end - grandtotal = grandtotal + count + end) + if length > 50 then + length = 50 + end + local fmt = string.formatters["%4.9k %4.9k %3.3k %8i %-" .. length .. "s %4i %s"] + for i=1,#dataset do + local data = dataset[i] + local real = data[1] + local total = data[2] + local count = data[3] + local name = data[4] + local source = data[5] + local line = data[6] + local percent = real / wholetime + calls = calls + count functions = functions + 1 + name = gsub(name,"%s+"," ") + if #name > length then + name = sub(name,1,length) + end + printer(fmt(seconds(total),seconds(real),percent,count,name,line,source)) end - printer("\n") - printer(format("functions : % 10i\n", functions)) - printer(format("total : % 10i\n", total)) - printer(format("grand total: % 10i\n", grandtotal)) - printer(format("threshold : % 10i\n", threshold)) + printer("") + printer(format("functions : %i", functions)) + printer(format("calls : %i", calls)) + printer(format("overhead : %f", seconds(overhead/1000))) end -function debugger.savestats(filename,threshold) +function debugger.savestats(filename) local f = io.open(filename,'w') if f then - debugger.showstats(function(str) f:write(str) end,threshold) + debugger.showstats(function(str) f:write(str,"\n") end) f:close() end end function debugger.enable() - debug.sethook(hook,"c") + if nesting == 0 then + running = true + if initialize then + initialize() + end + sethook(hook,"cr") + local function dummy() end + local t = ticks() + for i=1,dummycalls do + dummy() + end + overhead = ticks() - t + end + if nesting > 0 then + nesting = nesting + 1 + end end function debugger.disable() - debug.sethook() - -- counters[debug.getinfo(2,"f").func] = nil + if nesting > 0 then + nesting = nesting - 1 + end + if nesting == 0 then + sethook() + end end -- debugger.enable() diff --git a/tex/context/base/mkiv/util-env.lua b/tex/context/base/mkiv/util-env.lua index fb3dda41f..0b832e72e 100644 --- a/tex/context/base/mkiv/util-env.lua +++ b/tex/context/base/mkiv/util-env.lua @@ -31,15 +31,23 @@ end -- setlocale(nil,nil) -- end -- --- function os.pushlocale(...) +-- function os.pushlocale(l,...) -- insert(stack, { --- collate = setlocale("collate"), --- ctype = setlocale("ctype"), --- monetary = setlocale("monetary"), --- numeric = setlocale("numeric"), --- time = setlocale("time"), +-- collate = setlocale(nil,"collate"), +-- ctype = setlocale(nil,"ctype"), +-- monetary = setlocale(nil,"monetary"), +-- numeric = setlocale(nil,"numeric"), +-- time = setlocale(nil,"time"), -- }) --- setlocale(...) +-- if l then +-- setlocale(l,...) +-- else +-- setlocale(status.lc_collate ,"collate"), +-- setlocale(status.lc_ctype ,"ctype"), +-- setlocale(status.lc_monetary,"monetary"), +-- setlocale(status.lc_numeric ,"numeric"), +-- setlocale(status.lc_time ,"time"), +-- end -- end -- -- function os.poplocale(...) @@ -51,6 +59,12 @@ end -- end -- end -- +-- function os.setlocale() +-- -- no way you can mess with it, use push/pop +-- end +-- +-- setlocale(nil,nil) -- setlocale("all","C") +-- -- end -- dirty tricks (we will replace the texlua call by luatex --luaonly) diff --git a/tex/context/base/mkiv/util-fil.lua b/tex/context/base/mkiv/util-fil.lua index cf97d9541..dba3b235c 100644 --- a/tex/context/base/mkiv/util-fil.lua +++ b/tex/context/base/mkiv/util-fil.lua @@ -101,9 +101,10 @@ function files.readinteger1(f) -- one byte end end -files.readcardinal1 = files.readbyte -- one byte -files.readcardinal = files.readcardinal1 -files.readinteger = files.readinteger1 +files.readcardinal1 = files.readbyte -- one byte +files.readcardinal = files.readcardinal1 +files.readinteger = files.readinteger1 +files.readsignedbyte = files.readinteger1 function files.readcardinal2(f) local a, b = byte(f:read(2),1,2) diff --git a/tex/context/base/mkiv/util-lib.lua b/tex/context/base/mkiv/util-lib.lua index 2601b2e57..e9a968228 100644 --- a/tex/context/base/mkiv/util-lib.lua +++ b/tex/context/base/mkiv/util-lib.lua @@ -6,9 +6,6 @@ if not modules then modules = { } end modules ['util-lib'] = { license = "see context related readme files", } --- This is experimental code for Hans and Luigi. Don't depend on it! There --- will be a plain variant. - --[[ The problem with library bindings is manyfold. They are of course platform @@ -73,29 +70,41 @@ and then without. ]]-- --- seems to be clua in recent texlive - -local gsub, find = string.gsub, string.find -local pathpart, nameonly, joinfile = file.pathpart, file.nameonly, file.join -local findfile, findfiles = resolvers and resolvers.findfile, resolvers and resolvers.findfiles - -local loaded = package.loaded +local type = type +local next = next +local pcall = pcall +local gsub = string.gsub +local find = string.find +local sort = table.sort +local pathpart = file.pathpart +local nameonly = file.nameonly +local joinfile = file.join +local removesuffix = file.removesuffix +local findfile = resolvers.findfile +local findfiles = resolvers.findfiles +local expandpaths = resolvers.expandedpathlistfromvariable +local qualifiedpath = file.is_qualified_path +local isfile = lfs.isfile -local report_swiglib = logs.reporter("swiglib") -local trace_swiglib = false trackers.register("resolvers.swiglib", function(v) trace_swiglib = v end) +local done = false -- We can check if there are more that one component, and if not, we can -- append 'core'. -local done = false - -local function requireswiglib(required,version) - local trace_swiglib = trace_swiglib or package.helpers.trace - local library = loaded[required] - if library == nil then - if trace_swiglib then - report_swiglib("requiring library %a with version %a",required,version or "any") +local function locate(required,version,trace,report,action) + if type(required) ~= "string" then + report("provide a proper library name") + return + end + if trace then + report("requiring library %a with version %a",required,version or "any") + end + local found_library = nil + if qualifiedpath(required) then + if isfile(required) then + found_library = required end + else -- initialize a few variables local required_full = gsub(required,"%.","/") -- package.helpers.lualibfile local required_path = pathpart(required_full) @@ -104,16 +113,16 @@ local function requireswiglib(required,version) local version = type(version) == "string" and version ~= "" and version or false local engine = environment.ownmain or false -- - if trace_swiglib and not done then - local list = resolvers.expandedpathlistfromvariable("lib") -- fresh, no reuse + if trace and not done then + local list = expandpaths("lib") -- fresh, no reuse for i=1,#list do - report_swiglib("tds path %i: %s",i,list[i]) + report("tds path %i: %s",i,list[i]) end end -- helpers local function found(locate,asked_library,how,...) - if trace_swiglib then - report_swiglib("checking %s: %a",how,asked_library) + if trace then + report("checking %s: %a",how,asked_library) end return locate(asked_library,...) end @@ -121,15 +130,15 @@ local function requireswiglib(required,version) local found = nil if version then local asked_library = joinfile(required_path,version,required_name) - if trace_swiglib then - report_swiglib("checking %s: %a","with version",asked_library) + if trace then + report("checking %s: %a","with version",asked_library) end found = locate(asked_library,...) end if not found or found == "" then local asked_library = joinfile(required_path,required_name) - if trace_swiglib then - report_swiglib("checking %s: %a","with version",asked_library) + if trace then + report("checking %s: %a","with version",asked_library) end found = locate(asked_library,...) end @@ -140,32 +149,32 @@ local function requireswiglib(required,version) -- match anyway. local function attempt(checkpattern) -- check cnf spec using name and version - if trace_swiglib then - report_swiglib("checking tds lib paths strictly") + if trace then + report("checking tds lib paths strictly") end local found = findfile and check(findfile,"lib") if found and (not checkpattern or find(found,checkpattern)) then return found end -- check cnf spec using wildcard - if trace_swiglib then - report_swiglib("checking tds lib paths with wildcard") + if trace then + report("checking tds lib paths with wildcard") end local asked_library = joinfile(required_path,".*",required_name) - if trace_swiglib then - report_swiglib("checking %s: %a","latest version",asked_library) + if trace then + report("checking %s: %a","latest version",asked_library) end local list = findfiles(asked_library,"lib",true) if list and #list > 0 then - table.sort(list) + sort(list) local found = list[#list] if found and (not checkpattern or find(found,checkpattern)) then return found end end -- Check lib paths using name and version. - if trace_swiglib then - report_swiglib("checking lib paths") + if trace then + report("checking lib paths") end package.extralibpath(environment.ownpath) local paths = package.libpaths() @@ -177,59 +186,80 @@ local function requireswiglib(required,version) end return false end - local found_library = nil if engine then - if trace_swiglib then - report_swiglib("attemp 1, engine %a",engine) + if trace then + report("attemp 1, engine %a",engine) end found_library = attempt("/"..engine.."/") if not found_library then - if trace_swiglib then - report_swiglib("attemp 2, no engine",asked_library) + if trace then + report("attemp 2, no engine",asked_library) end found_library = attempt() end else found_library = attempt() end - -- load and initialize when found - if not found_library then - if trace_swiglib then - report_swiglib("not found: %a",required) - end - library = false - else - local path = pathpart(found_library) - local base = nameonly(found_library) - dir.push(path) - if trace_swiglib then - report_swiglib("found: %a",found_library) - end - local message = nil - local opener = "luaopen_" .. required_base - library, message = package.loadlib(found_library,opener) - local libtype = type(library) - if libtype == "function" then - library = library() - else - report_swiglib("load error: %a returns %a, message %a, library %a",opener,libtype,(string.gsub(message or "no message","[%s]+$","")),found_library or "no library") - library = false - end - dir.pop() - end - -- cache result - if not library then - report_swiglib("unknown: %a",required) - elseif trace_swiglib then - report_swiglib("stored: %a",required) + end + -- load and initialize when found + if not found_library then + if trace then + report("not found: %a",required) end - loaded[required] = library + library = false else - report_swiglib("reused: %a",required) + if trace then + report("found: %a",found_library) + end + local message, result = action(found_library,required_base) + if result then + library = result + else + library = false + report("load error: message %a, library %a",tostring(message),found_library or "no library") + end + end + if not library then + report("unknown: %a",required) + elseif trace then + report("stored: %a",required) end return library end +do + + local report_swiglib = logs.reporter("swiglib") + local trace_swiglib = false + local savedrequire = require + local loadedlibs = { } + + trackers.register("resolvers.swiglib", function(v) trace_swiglib = v end) + + function requireswiglib(required,version) + local library = loadedlibs[library] + if library == nil then + local trace_swiglib = trace_swiglib or package.helpers.trace + library = locate(required,version,trace_swiglib,report_swiglib,function(name,base) + dir.push(pathpart(name)) + local opener = "luaopen_" .. base + local library, message = package.loadlib(name,opener) + local libtype = type(library) + if libtype == "function" then + library = library() + message = true + else + report_swiglib("load error: %a returns %a, message %a, library %a",opener,libtype,(string.gsub(message or "no message","[%s]+$","")),found_library or "no library") + library = false + end + dir.pop() + return message, library + end) + loadedlibs[required] = library or false + end + return library + end + --[[ For convenience we make the require loader function swiglib aware. Alternatively @@ -237,15 +267,13 @@ we could put the specific loader in the global namespace. ]]-- -local savedrequire = require - -function require(name,version) - if find(name,"^swiglib%.") then - return requireswiglib(name,version) - else - return savedrequire(name) + function require(name,version) + if find(name,"^swiglib%.") then + return requireswiglib(name,version) + else + return savedrequire(name) + end end -end --[[ @@ -255,43 +283,87 @@ recommended loader. ]]-- -local swiglibs = { } -local initializer = "core" + local swiglibs = { } + local initializer = "core" -function swiglib(name,version) - local library = swiglibs[name] - if not library then - statistics.starttiming(swiglibs) - if trace_swiglib then - report_swiglib("loading %a",name) - end - if not find(name,"%." .. initializer .. "$") then - fullname = "swiglib." .. name .. "." .. initializer - else - fullname = "swiglib." .. name + function swiglib(name,version) + local library = swiglibs[name] + if not library then + statistics.starttiming(swiglibs) + if trace_swiglib then + report_swiglib("loading %a",name) + end + if not find(name,"%." .. initializer .. "$") then + fullname = "swiglib." .. name .. "." .. initializer + else + fullname = "swiglib." .. name + end + library = requireswiglib(fullname,version) + swiglibs[name] = library + statistics.stoptiming(swiglibs) end - library = requireswiglib(fullname,version) - swiglibs[name] = library - statistics.stoptiming(swiglibs) + return library end - return library + + statistics.register("used swiglibs", function() + if next(swiglibs) then + return string.format("%s, initial load time %s seconds",table.concat(table.sortedkeys(swiglibs)," "),statistics.elapsedtime(swiglibs)) + end + end) + end -statistics.register("used swiglibs", function() - if next(swiglibs) then - return string.format("%s, initial load time %s seconds",table.concat(table.sortedkeys(swiglibs)," "),statistics.elapsedtime(swiglibs)) +if FFISUPPORTED and ffi and ffi.load then + +--[[ + +We use the same lookup logic for ffi loading. + +]]-- + + local report_ffilib = logs.reporter("ffilib") + local trace_ffilib = false + local savedffiload = ffi.load + + trackers.register("resolvers.ffilib", function(v) trace_ffilib = v end) + + function ffilib(required,version) + return locate(required,version,trace_ffilib,report_ffilib,function(name) + local message, library = pcall(savedffiload,removesuffix(name)) + if type(library) == "userdata" then + return message, library + else + return message, false + end + end) end -end) + + function ffi.load(name) + local library = ffilib(name) + if type(library) == "userdata" then + return library + else + report_ffilib("trying to load %a using normal loader",name) + return savedffiload(name) + end + end + +end --[[ -So, we now have: +-- So, we now have: + +trackers.enable("resolvers.ffilib") +trackers.enable("resolvers.swiglib") local gm = require("swiglib.gmwand.core") local gm = swiglib("gmwand.core") local sq = swiglib("mysql.core") local sq = swiglib("mysql.core","5.6") -Watch out, the last one is less explicit and lacks the swiglib prefix. +ffilib("libmysql","5.6.14") + +-- Watch out, the last one is less explicit and lacks the swiglib prefix. ]]-- diff --git a/tex/context/base/mkiv/util-str.lua b/tex/context/base/mkiv/util-str.lua index fb510257a..9e6be9999 100644 --- a/tex/context/base/mkiv/util-str.lua +++ b/tex/context/base/mkiv/util-str.lua @@ -449,6 +449,31 @@ function number.sparseexponent(f,n) return tostring(n) end +local hf = { } +local hs = { } + +setmetatable(hf, { __index = function(t,k) + local v = "%." .. k .. "f" + t[k] = v + return v +end } ) + +setmetatable(hs, { __index = function(t,k) + local v = "%" .. k .. "s" + t[k] = v + return v +end } ) + +function number.formattedfloat(n,b,a) + local s = format(hf[a],n) + local l = (b or 0) + (a or 0) + 1 + if #s < l then + return format(hs[l],s) + else + return s + end +end + local template = [[ %s %s @@ -479,6 +504,7 @@ local autodouble=string.autodouble local sequenced=table.sequenced local formattednumber=number.formatted local sparseexponent=number.sparseexponent +local formattedfloat=number.formattedfloat ]] else @@ -504,6 +530,7 @@ else sequenced = table.sequenced, formattednumber = number.formatted, sparseexponent = number.sparseexponent, + formattedfloat = number.formattedfloat } end @@ -521,6 +548,9 @@ setmetatable(arguments, { __index = }) local prefix_any = C((S("+- .") + R("09"))^0) +local prefix_sub = (C((S("+-") + R("09"))^0) + Cc(0)) + * P(".") + * (C((S("+-") + R("09"))^0) + Cc(0)) local prefix_tab = P("{") * C((1-P("}"))^0) * P("}") + C((1-R("az","AZ","09","%%"))^0) -- we've split all cases as then we can optimize them (let's omit the fuzzy u) @@ -594,6 +624,11 @@ local format_F = function(f) -- beware, no cast to number end end +local format_k = function(b,a) -- slow + n = n + 1 + return format("formattedfloat(a%s,%i,%i)",n,b or 0, a or 0) +end + local format_g = function(f) n = n + 1 return format("format('%%%sg',a%s)",f,n) @@ -855,6 +890,7 @@ local builder = Cs { "start", + V("S") -- new + V("Q") -- new + V("N") -- new + + V("k") -- new -- + V("r") + V("h") + V("H") + V("u") + V("U") @@ -894,6 +930,7 @@ local builder = Cs { "start", ["S"] = (prefix_any * P("S")) / format_S, -- %S => %s (tostring) ["Q"] = (prefix_any * P("Q")) / format_S, -- %Q => %q (tostring) ["N"] = (prefix_any * P("N")) / format_N, -- %N => tonumber (strips leading zeros) + ["k"] = (prefix_sub * P("k")) / format_k, -- %k => like f but with n.m ["c"] = (prefix_any * P("c")) / format_c, -- %c => utf character (extension to regular) ["C"] = (prefix_any * P("C")) / format_C, -- %c => U+.... utf character -- |