From fa5d18c2f98ae3bca364f3c19804bd42f8305512 Mon Sep 17 00:00:00 2001
From: Karl Berry
Some code may look a bit obscure but this has to do with the fact that we also use
+this code for testing and much code evolved in the transition from
The following code still has traces of intermediate font support where we handles +font encodings. Eventually font encoding went away but we kept some code around in +other modules.
+ +This version implements a node mode approach so that users can also more easily +add features.
+--ldx]]-- + +local fonts, logs, trackers, containers, resolvers = fonts, logs, trackers, containers, resolvers + +local next, type, tonumber = next, type, tonumber +local match, gmatch, lower, gsub, strip, find = string.match, string.gmatch, string.lower, string.gsub, string.strip, string.find +local char, byte, sub = string.char, string.byte, string.sub +local abs = math.abs +local bxor, rshift = bit32.bxor, bit32.rshift +local P, S, R, Cmt, C, Ct, Cs, lpegmatch, patterns = lpeg.P, lpeg.S, lpeg.R, lpeg.Cmt, lpeg.C, lpeg.Ct, lpeg.Cs, lpeg.match, lpeg.patterns +local derivetable = table.derive + +local trace_features = false trackers.register("afm.features", function(v) trace_features = v end) +local trace_indexing = false trackers.register("afm.indexing", function(v) trace_indexing = v end) +local trace_loading = false trackers.register("afm.loading", function(v) trace_loading = v end) +local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) + +local report_afm = logs.reporter("fonts","afm loading") + +local setmetatableindex = table.setmetatableindex + +local findbinfile = resolvers.findbinfile + +local definers = fonts.definers +local readers = fonts.readers +local constructors = fonts.constructors + +local afm = constructors.newhandler("afm") +local pfb = constructors.newhandler("pfb") +local otf = fonts.handlers.otf + +local otfreaders = otf.readers +local otfenhancers = otf.enhancers + +local afmfeatures = constructors.newfeatures("afm") +local registerafmfeature = afmfeatures.register + +afm.version = 1.505 -- incrementing this number one up will force a re-cache +afm.cache = containers.define("fonts", "afm", afm.version, true) +afm.autoprefixed = true -- this will become false some day (catches texnansi-blabla.*) + +afm.helpdata = { } -- set later on so no local for this +afm.syncspace = true -- when true, nicer stretch values + +local overloads = fonts.mappings.overloads + +local applyruntimefixes = fonts.treatments and fonts.treatments.applyfixes + +--[[ldx-- +We start with the basic reader which we give a name similar to the
+built in
We cache files. Caching is taken care of in the loader. We cheat a bit by adding +ligatures and kern information to the afm derived data. That way we can set them faster +when defining a font.
+ +We still keep the loading two phased: first we load the data in a traditional +fashion and later we transform it to sequences.
+--ldx]]-- + +local addkerns, unify, normalize, fixnames, addligatures, addtexligatures + +function afm.load(filename) + filename = resolvers.findfile(filename,'afm') or "" + if filename ~= "" and not fonts.names.ignoredfile(filename) then + local name = file.removesuffix(file.basename(filename)) + local data = containers.read(afm.cache,name) + local attr = lfs.attributes(filename) + local size, time = attr.size or 0, attr.modification or 0 + -- + local pfbfile = file.replacesuffix(name,"pfb") + local pfbname = resolvers.findfile(pfbfile,"pfb") or "" + if pfbname == "" then + pfbname = resolvers.findfile(file.basename(pfbfile),"pfb") or "" + end + local pfbsize, pfbtime = 0, 0 + if pfbname ~= "" then + local attr = lfs.attributes(pfbname) + pfbsize = attr.size or 0 + pfbtime = attr.modification or 0 + end + if not data or data.size ~= size or data.time ~= time or data.pfbsize ~= pfbsize or data.pfbtime ~= pfbtime then + report_afm("reading %a",filename) + data = readafm(filename) + if data then + if pfbname ~= "" then + data.resources.filename = resolvers.unresolve(pfbname) + get_indexes(data,pfbname) + elseif trace_loading then + report_afm("no pfb file for %a",filename) + -- data.resources.filename = "unset" -- better than loading the afm file + end + -- we now have all the data loaded + if trace_loading then + report_afm("unifying %a",filename) + end + unify(data,filename) + if trace_loading then + report_afm("add ligatures") -- there can be missing ones + end + addligatures(data) + if trace_loading then + report_afm("add extra kerns") + end + addkerns(data) + if trace_loading then + report_afm("normalizing") + end + normalize(data) + if trace_loading then + report_afm("fixing names") + end + fixnames(data) + if trace_loading then + report_afm("add tounicode data") + end + -- otfreaders.addunicodetable(data) -- only when not done yet + fonts.mappings.addtounicode(data,filename) + -- otfreaders.extend(data) + otfreaders.pack(data) + data.size = size + data.time = time + data.pfbsize = pfbsize + data.pfbtime = pfbtime + report_afm("saving %a in cache",name) + -- data.resources.unicodes = nil -- consistent with otf but here we save not much + data = containers.write(afm.cache, name, data) + data = containers.read(afm.cache,name) + end + end + if data then + -- constructors.addcoreunicodes(unicodes) + otfreaders.unpack(data) + otfreaders.expand(data) -- inline tables + otfreaders.addunicodetable(data) -- only when not done yet + otfenhancers.apply(data,filename,data) + if applyruntimefixes then + applyruntimefixes(filename,data) + end + end + return data + else + return nil + end +end + +local uparser = fonts.mappings.makenameparser() + +unify = function(data, filename) + local unicodevector = fonts.encodings.agl.unicodes -- loaded runtime in context + local unicodes = { } + local names = { } + local private = constructors.privateoffset + local descriptions = data.descriptions + for name, blob in next, data.characters do + local code = unicodevector[name] -- or characters.name_to_unicode[name] + if not code then + code = lpegmatch(uparser,name) + if not code then + code = private + private = private + 1 + report_afm("assigning private slot %U for unknown glyph name %a",code,name) + end + end + local index = blob.index + unicodes[name] = code + names[name] = index + blob.name = name + descriptions[code] = { + boundingbox = blob.boundingbox, + width = blob.width, + kerns = blob.kerns, + index = index, + name = name, + } + end + for unicode, description in next, descriptions do + local kerns = description.kerns + if kerns then + local krn = { } + for name, kern in next, kerns do + local unicode = unicodes[name] + if unicode then + krn[unicode] = kern + else + -- print(unicode,name) + end + end + description.kerns = krn + end + end + data.characters = nil + local resources = data.resources + local filename = resources.filename or file.removesuffix(file.basename(filename)) + resources.filename = resolvers.unresolve(filename) -- no shortcut + resources.unicodes = unicodes -- name to unicode + resources.marks = { } -- todo + -- resources.names = names -- name to index + resources.private = private +end + +local everywhere = { ["*"] = { ["*"] = true } } -- or: { ["*"] = { "*" } } +local noflags = { false, false, false, false } + +normalize = function(data) + local ligatures = setmetatableindex("table") + local kerns = setmetatableindex("table") + local extrakerns = setmetatableindex("table") + for u, c in next, data.descriptions do + local l = c.ligatures + local k = c.kerns + local e = c.extrakerns + if l then + ligatures[u] = l + for u, v in next, l do + l[u] = { ligature = v } + end + c.ligatures = nil + end + if k then + kerns[u] = k + for u, v in next, k do + k[u] = v -- { v, 0 } + end + c.kerns = nil + end + if e then + extrakerns[u] = e + for u, v in next, e do + e[u] = v -- { v, 0 } + end + c.extrakerns = nil + end + end + local features = { + gpos = { }, + gsub = { }, + } + local sequences = { + -- only filled ones + } + if next(ligatures) then + features.gsub.liga = everywhere + data.properties.hasligatures = true + sequences[#sequences+1] = { + features = { + liga = everywhere, + }, + flags = noflags, + name = "s_s_0", + nofsteps = 1, + order = { "liga" }, + type = "gsub_ligature", + steps = { + { + coverage = ligatures, + }, + }, + } + end + if next(kerns) then + features.gpos.kern = everywhere + data.properties.haskerns = true + sequences[#sequences+1] = { + features = { + kern = everywhere, + }, + flags = noflags, + name = "p_s_0", + nofsteps = 1, + order = { "kern" }, + type = "gpos_pair", + steps = { + { + format = "kern", + coverage = kerns, + }, + }, + } + end + if next(extrakerns) then + features.gpos.extrakerns = everywhere + data.properties.haskerns = true + sequences[#sequences+1] = { + features = { + extrakerns = everywhere, + }, + flags = noflags, + name = "p_s_1", + nofsteps = 1, + order = { "extrakerns" }, + type = "gpos_pair", + steps = { + { + format = "kern", + coverage = extrakerns, + }, + }, + } + end + -- todo: compress kerns + data.resources.features = features + data.resources.sequences = sequences +end + +fixnames = function(data) + for k, v in next, data.descriptions do + local n = v.name + local r = overloads[n] + if r then + local name = r.name + if trace_indexing then + report_afm("renaming characters %a to %a",n,name) + end + v.name = name + v.unicode = r.unicode + end + end +end + +--[[ldx-- +These helpers extend the basic table with extra ligatures, texligatures +and extra kerns. This saves quite some lookups later.
+--ldx]]-- + +local addthem = function(rawdata,ligatures) + if ligatures then + local descriptions = rawdata.descriptions + local resources = rawdata.resources + local unicodes = resources.unicodes + -- local names = resources.names + for ligname, ligdata in next, ligatures do + local one = descriptions[unicodes[ligname]] + if one then + for _, pair in next, ligdata do + local two, three = unicodes[pair[1]], unicodes[pair[2]] + if two and three then + local ol = one.ligatures + if ol then + if not ol[two] then + ol[two] = three + end + else + one.ligatures = { [two] = three } + end + end + end + end + end + end +end + +addligatures = function(rawdata) addthem(rawdata,afm.helpdata.ligatures ) end +addtexligatures = function(rawdata) addthem(rawdata,afm.helpdata.texligatures) end + +--[[ldx-- +We keep the extra kerns in separate kerning tables so that we can use +them selectively.
+--ldx]]-- + +-- This is rather old code (from the beginning when we had only tfm). If +-- we unify the afm data (now we have names all over the place) then +-- we can use shcodes but there will be many more looping then. But we +-- could get rid of the tables in char-cmp then. Als, in the generic version +-- we don't use the character database. (Ok, we can have a context specific +-- variant). + +addkerns = function(rawdata) -- using shcodes is not robust here + local descriptions = rawdata.descriptions + local resources = rawdata.resources + local unicodes = resources.unicodes + local function do_it_left(what) + if what then + for unicode, description in next, descriptions do + local kerns = description.kerns + if kerns then + local extrakerns + for complex, simple in next, what do + complex = unicodes[complex] + simple = unicodes[simple] + if complex and simple then + local ks = kerns[simple] + if ks and not kerns[complex] then + if extrakerns then + extrakerns[complex] = ks + else + extrakerns = { [complex] = ks } + end + end + end + end + if extrakerns then + description.extrakerns = extrakerns + end + end + end + end + end + local function do_it_copy(what) + if what then + for complex, simple in next, what do + complex = unicodes[complex] + simple = unicodes[simple] + if complex and simple then + local complexdescription = descriptions[complex] + if complexdescription then -- optional + local simpledescription = descriptions[complex] + if simpledescription then + local extrakerns + local kerns = simpledescription.kerns + if kerns then + for unicode, kern in next, kerns do + if extrakerns then + extrakerns[unicode] = kern + else + extrakerns = { [unicode] = kern } + end + end + end + local extrakerns = simpledescription.extrakerns + if extrakerns then + for unicode, kern in next, extrakerns do + if extrakerns then + extrakerns[unicode] = kern + else + extrakerns = { [unicode] = kern } + end + end + end + if extrakerns then + complexdescription.extrakerns = extrakerns + end + end + end + end + end + end + end + -- add complex with values of simplified when present + do_it_left(afm.helpdata.leftkerned) + do_it_left(afm.helpdata.bothkerned) + -- copy kerns from simple char to complex char unless set + do_it_copy(afm.helpdata.bothkerned) + do_it_copy(afm.helpdata.rightkerned) +end + +--[[ldx-- +The copying routine looks messy (and is indeed a bit messy).
+--ldx]]-- + +local function adddimensions(data) -- we need to normalize afm to otf i.e. indexed table instead of name + if data then + for unicode, description in next, data.descriptions do + local bb = description.boundingbox + if bb then + local ht, dp = bb[4], -bb[2] + if ht == 0 or ht < 0 then + -- no need to set it and no negative heights, nil == 0 + else + description.height = ht + end + if dp == 0 or dp < 0 then + -- no negative depths and no negative depths, nil == 0 + else + description.depth = dp + end + end + end + end +end + +local function copytotfm(data) + if data and data.descriptions then + local metadata = data.metadata + local resources = data.resources + local properties = derivetable(data.properties) + local descriptions = derivetable(data.descriptions) + local goodies = derivetable(data.goodies) + local characters = { } + local parameters = { } + local unicodes = resources.unicodes + -- + for unicode, description in next, data.descriptions do -- use parent table + characters[unicode] = { } + end + -- + local filename = constructors.checkedfilename(resources) + local fontname = metadata.fontname or metadata.fullname + local fullname = metadata.fullname or metadata.fontname + local endash = 0x0020 -- space + local emdash = 0x2014 + local spacer = "space" + local spaceunits = 500 + -- + local monospaced = metadata.monospaced + local charwidth = metadata.charwidth + local italicangle = metadata.italicangle + local charxheight = metadata.xheight and metadata.xheight > 0 and metadata.xheight + properties.monospaced = monospaced + parameters.italicangle = italicangle + parameters.charwidth = charwidth + parameters.charxheight = charxheight + -- same as otf + if properties.monospaced then + if descriptions[endash] then + spaceunits, spacer = descriptions[endash].width, "space" + end + if not spaceunits and descriptions[emdash] then + spaceunits, spacer = descriptions[emdash].width, "emdash" + end + if not spaceunits and charwidth then + spaceunits, spacer = charwidth, "charwidth" + end + else + if descriptions[endash] then + spaceunits, spacer = descriptions[endash].width, "space" + end + if not spaceunits and charwidth then + spaceunits, spacer = charwidth, "charwidth" + end + end + spaceunits = tonumber(spaceunits) + if spaceunits < 200 then + -- todo: warning + end + -- + parameters.slant = 0 + parameters.space = spaceunits + parameters.space_stretch = 500 + parameters.space_shrink = 333 + parameters.x_height = 400 + parameters.quad = 1000 + -- + if italicangle and italicangle ~= 0 then + parameters.italicangle = italicangle + parameters.italicfactor = math.cos(math.rad(90+italicangle)) + parameters.slant = - math.tan(italicangle*math.pi/180) + end + if monospaced then + parameters.space_stretch = 0 + parameters.space_shrink = 0 + elseif afm.syncspace then + parameters.space_stretch = spaceunits/2 + parameters.space_shrink = spaceunits/3 + end + parameters.extra_space = parameters.space_shrink + if charxheight then + parameters.x_height = charxheight + else + -- same as otf + local x = 0x0078 -- x + if x then + local x = descriptions[x] + if x then + parameters.x_height = x.height + end + end + -- + end + local fd = data.fontdimens + if fd and fd[8] and fd[9] and fd[10] then -- math + for k,v in next, fd do + parameters[k] = v + end + end + -- + parameters.designsize = (metadata.designsize or 10)*65536 + parameters.ascender = abs(metadata.ascender or 0) + parameters.descender = abs(metadata.descender or 0) + parameters.units = 1000 + -- + properties.spacer = spacer + properties.encodingbytes = 2 + properties.format = fonts.formats[filename] or "type1" + properties.filename = filename + properties.fontname = fontname + properties.fullname = fullname + properties.psname = fullname + properties.name = filename or fullname or fontname + -- + if next(characters) then + return { + characters = characters, + descriptions = descriptions, + parameters = parameters, + resources = resources, + properties = properties, + goodies = goodies, + } + end + end + return nil +end + +--[[ldx-- +Originally we had features kind of hard coded for
As soon as we could intercept the
Here comes the implementation of a few features. We only implement +those that make sense for this format.
+--ldx]]-- + +local function prepareligatures(tfmdata,ligatures,value) + if value then + local descriptions = tfmdata.descriptions + local hasligatures = false + for unicode, character in next, tfmdata.characters do + local description = descriptions[unicode] + local dligatures = description.ligatures + if dligatures then + local cligatures = character.ligatures + if not cligatures then + cligatures = { } + character.ligatures = cligatures + end + for unicode, ligature in next, dligatures do + cligatures[unicode] = { + char = ligature, + type = 0 + } + end + hasligatures = true + end + end + tfmdata.properties.hasligatures = hasligatures + end +end + +local function preparekerns(tfmdata,kerns,value) + if value then + local rawdata = tfmdata.shared.rawdata + local resources = rawdata.resources + local unicodes = resources.unicodes + local descriptions = tfmdata.descriptions + local haskerns = false + for u, chr in next, tfmdata.characters do + local d = descriptions[u] + local newkerns = d[kerns] + if newkerns then + local kerns = chr.kerns + if not kerns then + kerns = { } + chr.kerns = kerns + end + for k,v in next, newkerns do + local uk = unicodes[k] + if uk then + kerns[uk] = v + end + end + haskerns = true + end + end + tfmdata.properties.haskerns = haskerns + end +end + +local list = { + -- [0x0022] = 0x201D, + [0x0027] = 0x2019, + -- [0x0060] = 0x2018, +} + +local function texreplacements(tfmdata,value) + local descriptions = tfmdata.descriptions + local characters = tfmdata.characters + for k, v in next, list do + characters [k] = characters [v] -- we forget about kerns + descriptions[k] = descriptions[v] -- we forget about kerns + end +end + +-- local function ligatures (tfmdata,value) prepareligatures(tfmdata,'ligatures', value) end +-- local function texligatures(tfmdata,value) prepareligatures(tfmdata,'texligatures',value) end +-- local function kerns (tfmdata,value) preparekerns (tfmdata,'kerns', value) end +local function extrakerns (tfmdata,value) preparekerns (tfmdata,'extrakerns', value) end + +local function setmode(tfmdata,value) + if value then + tfmdata.properties.mode = lower(value) + end +end + +registerafmfeature { + name = "mode", + description = "mode", + initializers = { + base = setmode, + node = setmode, + } +} + +registerafmfeature { + name = "features", + description = "features", + default = true, + initializers = { + node = otf.nodemodeinitializer, + base = otf.basemodeinitializer, + }, + processors = { + node = otf.featuresprocessor, + } +} + +-- readers + +local check_tfm = readers.check_tfm + +fonts.formats.afm = "type1" +fonts.formats.pfb = "type1" + +local function check_afm(specification,fullname) + local foundname = findbinfile(fullname, 'afm') or "" -- just to be sure + if foundname == "" then + foundname = fonts.names.getfilename(fullname,"afm") or "" + end + if foundname == "" and afm.autoprefixed then + local encoding, shortname = match(fullname,"^(.-)%-(.*)$") -- context: encoding-name.* + if encoding and shortname and fonts.encodings.known[encoding] then + shortname = findbinfile(shortname,'afm') or "" -- just to be sure + if shortname ~= "" then + foundname = shortname + if trace_defining then + report_afm("stripping encoding prefix from filename %a",afmname) + end + end + end + end + if foundname ~= "" then + specification.filename = foundname + specification.format = "afm" + return read_from_afm(specification) + end +end + +function readers.afm(specification,method) + local fullname, tfmdata = specification.filename or "", nil + if fullname == "" then + local forced = specification.forced or "" + if forced ~= "" then + tfmdata = check_afm(specification,specification.name .. "." .. forced) + end + if not tfmdata then + method = method or definers.method or "afm or tfm" + if method == "tfm" then + tfmdata = check_tfm(specification,specification.name) + elseif method == "afm" then + tfmdata = check_afm(specification,specification.name) + elseif method == "tfm or afm" then + tfmdata = check_tfm(specification,specification.name) or check_afm(specification,specification.name) + else -- method == "afm or tfm" or method == "" then + tfmdata = check_afm(specification,specification.name) or check_tfm(specification,specification.name) + end + end + else + tfmdata = check_afm(specification,fullname) + end + return tfmdata +end + +function readers.pfb(specification,method) -- only called when forced + local original = specification.specification + if trace_defining then + report_afm("using afm reader for %a",original) + end + specification.specification = gsub(original,"%.pfb",".afm") + specification.forced = "afm" + return readers.afm(specification,method) +end diff --git a/Master/texmf-dist/tex/context/base/mkiv/font-otc.lua b/Master/texmf-dist/tex/context/base/mkiv/font-otc.lua index 871b6f13c97..be9ba86604b 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/font-otc.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/font-otc.lua @@ -61,8 +61,6 @@ local function addfeature(data,feature,specifications) end -- feature has to be unique but the name entry wins eventually - -- todo alse gpos - local fontfeatures = resources.features or everywhere local unicodes = resources.unicodes local splitter = lpeg.splitter(" ",unicodes) @@ -529,6 +527,7 @@ end otf.enhancers.addfeature = addfeature local extrafeatures = { } +local knownfeatures = { } function otf.addfeature(name,specification) if type(name) == "table" then @@ -536,16 +535,31 @@ function otf.addfeature(name,specification) name = specification.name end if type(name) == "string" then - extrafeatures[name] = specification + local slot = knownfeatures[name] + if slot then + -- we overload one + else + slot = #extrafeatures + 1 + knownfeatures[name] = slot + end + specification.name = name -- to be sure + extrafeatures[slot] = specification end end +-- for feature, specification in next, extrafeatures do +-- addfeature(data,feature,specification) +-- end + local function enhance(data,filename,raw) - for feature, specification in next, extrafeatures do - addfeature(data,feature,specification) + for slot=1,#extrafeatures do + local specification = extrafeatures[slot] + addfeature(data,specification.name,specification) end end +-- otf.enhancers.enhance = enhance + otf.enhancers.register("check extra features",enhance) -- tlig -- @@ -574,6 +588,7 @@ local tlig_specification = { otf.addfeature("tlig",tlig_specification) registerotffeature { + -- this makes it a known feature (in tables) name = 'tlig', description = 'tex ligatures', } @@ -598,6 +613,7 @@ local trep_specification = { otf.addfeature("trep",trep_specification) registerotffeature { + -- this makes it a known feature (in tables) name = 'trep', description = 'tex replacements', } @@ -699,6 +715,7 @@ local anum_specification = { otf.addfeature("anum",anum_specification) -- todo: only when there is already an arab script feature registerotffeature { + -- this makes it a known feature (in tables) name = 'anum', description = 'arabic digits', } diff --git a/Master/texmf-dist/tex/context/base/mkiv/font-otj.lua b/Master/texmf-dist/tex/context/base/mkiv/font-otj.lua index 6ff80d88df8..b65a9db6627 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/font-otj.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/font-otj.lua @@ -1255,11 +1255,11 @@ local function inject_everything(head,where) insert_node_after(pre,n,newkern(rightkern)) done = true end - end - if hasmarks then - local pm = i.markbasenode - if pm then - processmark(pm,current,i) + if hasmarks then + local pm = i.markbasenode + if pm then + processmark(pm,current,i) + end end end end @@ -1287,11 +1287,11 @@ local function inject_everything(head,where) insert_node_after(post,n,newkern(rightkern)) done = true end - end - if hasmarks then - local pm = i.markbasenode - if pm then - processmark(pm,current,i) + if hasmarks then + local pm = i.markbasenode + if pm then + processmark(pm,current,i) + end end end end @@ -1319,11 +1319,11 @@ local function inject_everything(head,where) insert_node_after(replace,n,newkern(rightkern)) done = true end - end - if hasmarks then - local pm = i.markbasenode - if pm then - processmark(pm,current,i) + if hasmarks then + local pm = i.markbasenode + if pm then + processmark(pm,current,i) + end end end end diff --git a/Master/texmf-dist/tex/context/base/mkiv/font-otl.lua b/Master/texmf-dist/tex/context/base/mkiv/font-otl.lua index f7b6eb5ae5f..01342a9be6e 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/font-otl.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/font-otl.lua @@ -101,6 +101,12 @@ registerdirective("fonts.otf.loader.forcenotdef", function(v) forcenotdef = -- end -- end +-- Enhancers are used to apply fixes and extensions to fonts. For instance, we use them +-- to implement tlig and trep features. They are not neccessarily bound to opentype +-- fonts but can also apply to type one fonts, given that they obey the structure of an +-- opentype font. They are not to be confused with format specific features but maybe +-- some are so generic that they might eventually move to this mechanism. + local ordered_enhancers = { "check extra features", } @@ -302,7 +308,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone -- enhancers.apply(data,filename,data) -- - constructors.addcoreunicodes(unicodes) + -- constructors.addcoreunicodes(data.resources.unicodes) -- still needed ? -- if applyruntimefixes then applyruntimefixes(filename,data) diff --git a/Master/texmf-dist/tex/context/base/mkiv/font-oto.lua b/Master/texmf-dist/tex/context/base/mkiv/font-oto.lua index b7ee717c949..23beba7876d 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/font-oto.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/font-oto.lua @@ -450,3 +450,5 @@ registerotffeature { base = featuresinitializer, } } + +otf.basemodeinitializer = featuresinitializer diff --git a/Master/texmf-dist/tex/context/base/mkiv/font-ots.lua b/Master/texmf-dist/tex/context/base/mkiv/font-ots.lua index 21225c2274f..c173de2be5a 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/font-ots.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/font-ots.lua @@ -3652,6 +3652,9 @@ registerotffeature { } } +otf.nodemodeinitializer = featuresinitializer +otf.featuresprocessor = featuresprocessor + -- This can be used for extra handlers, but should be used with care! otf.handlers = handlers -- used in devanagari diff --git a/Master/texmf-dist/tex/context/base/mkiv/lang-def.mkiv b/Master/texmf-dist/tex/context/base/mkiv/lang-def.mkiv index e4906096e0e..5e40a33b030 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/lang-def.mkiv +++ b/Master/texmf-dist/tex/context/base/mkiv/lang-def.mkiv @@ -341,7 +341,7 @@ \c!rightquotation=\upperrightdoubleninequote, \c!date={\v!year,.,\space,\v!month,\space,\v!day,.}] -\installlanguage [\s!finish] [\s!fi] +\installlanguage [\s!finnish] [\s!fi] \installlanguage [\s!hungarian] [\s!hu] % Altaic Languages: Uigur, Uzbek, Azeri/Azerbaijani, Chuvash, diff --git a/Master/texmf-dist/tex/context/base/mkiv/lang-ini.lua b/Master/texmf-dist/tex/context/base/mkiv/lang-ini.lua index eb7e32b89f3..00fdb3f0973 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/lang-ini.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/lang-ini.lua @@ -40,8 +40,8 @@ local prehyphenchar = lang.prehyphenchar -- global per language local posthyphenchar = lang.posthyphenchar -- global per language local preexhyphenchar = lang.preexhyphenchar -- global per language local postexhyphenchar = lang.postexhyphenchar -- global per language -local lefthyphenmin = lang.lefthyphenmin -local righthyphenmin = lang.righthyphenmin +----- lefthyphenmin = lang.lefthyphenmin +----- righthyphenmin = lang.righthyphenmin local sethjcode = lang.sethjcode local uccodes = characters.uccodes @@ -344,8 +344,8 @@ function languages.prehyphenchar (what) return prehyphenchar (tolang(what)) function languages.posthyphenchar (what) return posthyphenchar (tolang(what)) end function languages.preexhyphenchar (what) return preexhyphenchar (tolang(what)) end function languages.postexhyphenchar(what) return postexhyphenchar(tolang(what)) end -function languages.lefthyphenmin (what) return lefthyphenmin (tolang(what)) end -function languages.righthyphenmin (what) return righthyphenmin (tolang(what)) end +-------- languages.lefthyphenmin (what) return lefthyphenmin (tolang(what)) end +-------- languages.righthyphenmin (what) return righthyphenmin (tolang(what)) end -- e['implementer']= 'imple{m}{-}{-}menter' -- e['manual'] = 'man{}{}{}' diff --git a/Master/texmf-dist/tex/context/base/mkiv/lang-ini.mkiv b/Master/texmf-dist/tex/context/base/mkiv/lang-ini.mkiv index 8ff41b28792..214ce8ca38a 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/lang-ini.mkiv +++ b/Master/texmf-dist/tex/context/base/mkiv/lang-ini.mkiv @@ -518,7 +518,7 @@ \let\dohyphens\relax -\unexpanded\def\lang_basics_synchronize_min_max +\unexpanded\def\lang_basics_synchronize_min_max % maybe store this at the lua end {% these values are stored along with glyph nodes \lefthyphenmin \numexpr0\languageparameter\s!lefthyphenmin +\hyphenminoffset\relax \righthyphenmin\numexpr0\languageparameter\s!righthyphenmin+\hyphenminoffset\relax diff --git a/Master/texmf-dist/tex/context/base/mkiv/lpdf-ini.lua b/Master/texmf-dist/tex/context/base/mkiv/lpdf-ini.lua index 2149f4729a4..f0b919d4ee0 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/lpdf-ini.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/lpdf-ini.lua @@ -1059,13 +1059,30 @@ do return timestamp end + function lpdf.settime(n) + if n then + n = converters.totime(n) + if n then + converters.settime(n) + timestamp = os.date("%Y-%m-%dT%X",os.time(n)) .. os.timezone(true) + end + end + return timestamp + end + + lpdf.settime(tonumber(resolvers.variable("start_time")) or tonumber(resolvers.variable("SOURCE_DATE_EPOCH"))) -- bah + function lpdf.pdftimestamp(str) local Y, M, D, h, m, s, Zs, Zh, Zm = match(str,"^(%d%d%d%d)%-(%d%d)%-(%d%d)T(%d%d):(%d%d):(%d%d)([%+%-])(%d%d):(%d%d)$") return Y and format("D:%s%s%s%s%s%s%s%s'%s'",Y,M,D,h,m,s,Zs,Zh,Zm) end - function lpdf.id() - return format("%s.%s",tex.jobname,timestamp) + function lpdf.id(nodate) + if nodate then + return tex.jobname + else + return format("%s.%s",tex.jobname,timestamp) + end end end diff --git a/Master/texmf-dist/tex/context/base/mkiv/lpdf-xmp.lua b/Master/texmf-dist/tex/context/base/mkiv/lpdf-xmp.lua index b8170319c11..c8b86d38464 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/lpdf-xmp.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/lpdf-xmp.lua @@ -41,7 +41,7 @@ local mapping = { ["ConTeXt.Time"] = { "date", "rdf:Description/pdfx:ConTeXt.Time" }, ["ConTeXt.Url"] = { "context", "rdf:Description/pdfx:ConTeXt.Url" }, ["ConTeXt.Version"] = { "context", "rdf:Description/pdfx:ConTeXt.Version" }, - ["ID"] = { "date", "rdf:Description/pdfx:ID" }, -- has date + ["ID"] = { "id", "rdf:Description/pdfx:ID" }, -- has date ["PTEX.Fullbanner"] = { "metadata","rdf:Description/pdfx:PTEX.Fullbanner" }, -- Adobe PDF schema ["Keywords"] = { "metadata","rdf:Description/pdf:Keywords" }, @@ -60,8 +60,8 @@ local mapping = { ["ModDate"] = { "date", "rdf:Description/xmp:ModDate" }, -- dummy ["ModifyDate"] = { "date", "rdf:Description/xmp:ModifyDate" }, -- XMP Media Management schema - ["DocumentID"] = { "date", "rdf:Description/xmpMM:DocumentID" }, -- uuid - ["InstanceID"] = { "date", "rdf:Description/xmpMM:InstanceID" }, -- uuid + ["DocumentID"] = { "id", "rdf:Description/xmpMM:DocumentID" }, -- uuid + ["InstanceID"] = { "id", "rdf:Description/xmpMM:InstanceID" }, -- uuid ["RenditionClass"] = { "pdf", "rdf:Description/xmpMM:RenditionClass" }, -- PDF/X-4 ["VersionID"] = { "pdf", "rdf:Description/xmpMM:VersionID" }, -- PDF/X-4 -- additional entries @@ -105,32 +105,66 @@ local included = table.setmetatableindex( { return true end) -directives.register("backend.nodates", function(v) - included.date = not v +function lpdf.settrailerid(v) if v then - report_info("no date/time information will be added to the PDF file") - end -end) - -directives.register("backend.trailerid", function(v) - if v then - if toboolean(v) or v == "" then + local b = toboolean(v) or v == "" + if b then v = "This file is processed by ConTeXt and LuaTeX." else v = tostring(v) end local h = md5.HEX(v) - report_info("using hashed trailer id %a (%a)",v,h) + if b then + report_info("using frozen trailer id") + else + report_info("using hashed trailer id %a (%a)",v,h) + end pdf.settrailerid(format("[<%s> <%s>]",h,h)) end -end) +end + +function lpdf.setdates(v) + local t = type(v) + if t == "number" or t == "string" then + t = converters.totime(v) + if t then + included.date = true + included.id = "fake" + report_info("forced date/time information %a will be used",lpdf.settime(t)) + lpdf.settrailerid(false) + return + end + end + v = toboolean(v) + included.date = v + if v then + included.id = true + else + report_info("no date/time but fake id information will be added") + lpdf.settrailerid(true) + included.id = "fake" + -- maybe: lpdf.settime(231631200) -- 1975-05-05 % first entry of knuth about tex mentioned in DT + end +end + +function lpdf.id() -- overload of ini + local banner = tex.jobname + if included.date then + return format("%s.%s",banner,lpdf.timestamp()) + else + return banner + end +end + +directives.register("backend.trailerid", lpdf.settrailerid) +directives.register("backend.date", lpdf.setdates) local function permitdetail(what) local m = mapping[what] if m then return included[m[1]] and m[2] else - return included[what] + return included[what] and true or false end end @@ -214,22 +248,34 @@ end -- flushing -local t = { } for i=1,24 do t[i] = random() end +local function randomstring(n) + local t = { } + for i=1,n do + t[i] = char(96 + random(26)) + end + return concat(t) +end + +randomstring(26) -- kind of initializes and kicks off random local function flushxmpinfo() commands.pushrandomseed() commands.setrandomseed(os.time()) - local t = { } for i=1,24 do t[i] = char(96 + random(26)) end - local packetid = concat(t) - - local documentid = format("uuid:%s",os.uuid()) - local instanceid = format("uuid:%s",os.uuid()) + local packetid = "no unique packet id here" -- 24 chars + local documentid = "no unique document id here" + local instanceid = "no unique instance id here" local producer = format("LuaTeX-%0.2f.%s",status.luatex_version/100,status.luatex_revision) local creator = "LuaTeX + ConTeXt MkIV" local time = lpdf.timestamp() local fullbanner = status.banner + if included.id ~= "fake" then + packetid = randomstring(24) + documentid = "uuid:%s" .. os.uuid() + instanceid = "uuid:%s" .. os.uuid() + end + pdfaddxmpinfo("DocumentID", documentid) pdfaddxmpinfo("InstanceID", instanceid) pdfaddxmpinfo("Producer", producer) diff --git a/Master/texmf-dist/tex/context/base/mkiv/luat-cnf.lua b/Master/texmf-dist/tex/context/base/mkiv/luat-cnf.lua index 83622ef5399..9d37df7bb10 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/luat-cnf.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/luat-cnf.lua @@ -42,6 +42,7 @@ luatex = luatex or { } texconfig.kpse_init = false texconfig.shell_escape = 't' +---------.start_time = tonumber(os.getenv("SOURCE_DATE_EPOCH")) -- not used in context -- as soon as possible diff --git a/Master/texmf-dist/tex/context/base/mkiv/m-oldotf.mkiv b/Master/texmf-dist/tex/context/base/mkiv/m-oldotf.mkiv index c7c468d93ec..313f9f48459 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/m-oldotf.mkiv +++ b/Master/texmf-dist/tex/context/base/mkiv/m-oldotf.mkiv @@ -25,6 +25,7 @@ "font-otc", "font-oth", "font-odv", + "font-one", "font-map", "font-fbk", "font-gds", diff --git a/Master/texmf-dist/tex/context/base/mkiv/meta-ini.mkiv b/Master/texmf-dist/tex/context/base/mkiv/meta-ini.mkiv index 9f53aea69f6..d0fff83dfd6 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/meta-ini.mkiv +++ b/Master/texmf-dist/tex/context/base/mkiv/meta-ini.mkiv @@ -486,6 +486,10 @@ \startMPextensions string contextversion; contextversion:="\contextversion"; % expanded + minute:=\the\normaltime mod 60; + hour:=\the\normaltime div 60; + year:=\the\normalyear; + month:=\the\normalmonth; \stopMPextensions %D \macros diff --git a/Master/texmf-dist/tex/context/base/mkiv/mult-sys.mkiv b/Master/texmf-dist/tex/context/base/mkiv/mult-sys.mkiv index 01a1674dced..88e956d66e9 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/mult-sys.mkiv +++ b/Master/texmf-dist/tex/context/base/mkiv/mult-sys.mkiv @@ -48,7 +48,7 @@ \definesystemconstant {danish} \definesystemconstant {da} \definesystemconstant {dutch} \definesystemconstant {nl} \definesystemconstant {english} \definesystemconstant {en} -\definesystemconstant {finish} \definesystemconstant {fi} +\definesystemconstant {finnish} \definesystemconstant {fi} \definesystemconstant {french} \definesystemconstant {fr} \definesystemconstant {german} \definesystemconstant {de} \definesystemconstant {hungarian} \definesystemconstant {hu} diff --git a/Master/texmf-dist/tex/context/base/mkiv/node-fnt.lua b/Master/texmf-dist/tex/context/base/mkiv/node-fnt.lua index 7f0d239979a..e77280c372e 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/node-fnt.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/node-fnt.lua @@ -17,9 +17,12 @@ local trace_characters = false trackers.register("nodes.characters", function( local trace_fontrun = false trackers.register("nodes.fontrun", function(v) trace_fontrun = v end) local trace_variants = false trackers.register("nodes.variants", function(v) trace_variants = v end) -local force_discrun = true directives.register("nodes.discrun", function(v) force_discrun = v end) -local force_boundaryrun = true directives.register("nodes.boundaryrun", function(v) force_boundaryrun = v end) -local force_basepass = true directives.register("nodes.basepass", function(v) force_basepass = v end) +-- bad namespace for directives + +local force_discrun = true directives.register("nodes.discrun", function(v) force_discrun = v end) +local force_boundaryrun = true directives.register("nodes.boundaryrun", function(v) force_boundaryrun = v end) +local force_basepass = true directives.register("nodes.basepass", function(v) force_basepass = v end) +local keep_redundant = false directives.register("nodes.keepredundant",function(v) keep_redundant = v end) local report_fonts = logs.reporter("fonts","processing") @@ -271,6 +274,12 @@ function handlers.characters(head) end end end + elseif keep_redundant then + -- go on, can be used for tracing + elseif not redundant then + redundant = { n } + else + redundant[#redundant+1] = n end end end diff --git a/Master/texmf-dist/tex/context/base/mkiv/status-files.pdf b/Master/texmf-dist/tex/context/base/mkiv/status-files.pdf index 5d4c84ce108..6ea8ed23fd9 100644 Binary files a/Master/texmf-dist/tex/context/base/mkiv/status-files.pdf and b/Master/texmf-dist/tex/context/base/mkiv/status-files.pdf differ diff --git a/Master/texmf-dist/tex/context/base/mkiv/status-lua.pdf b/Master/texmf-dist/tex/context/base/mkiv/status-lua.pdf index 84aba5a7fd9..cb96b7185ed 100644 Binary files a/Master/texmf-dist/tex/context/base/mkiv/status-lua.pdf and b/Master/texmf-dist/tex/context/base/mkiv/status-lua.pdf differ diff --git a/Master/texmf-dist/tex/context/base/mkiv/typo-tal.lua b/Master/texmf-dist/tex/context/base/mkiv/typo-tal.lua index a67cd0a4b47..21c6794c47a 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/typo-tal.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/typo-tal.lua @@ -200,10 +200,10 @@ function characteralign.handler(originalhead,where) while current do local char, id = isglyph(current) if char then - local font = getfont(current) - -- local unicode = unicodes[font][char] - local unicode = fontcharacters[font][char].unicode or char -- ignore tables - if not unicode then + local font = getfont(current) + local data = fontcharacters[font][char] + local unicode = data and data.unicode or char -- ignore tables + if not unicode then -- type(unicode) ~= "number" -- no unicode so forget about it elseif unicode == separator then c = current diff --git a/Master/texmf-dist/tex/context/base/mkiv/typo-wrp.mkiv b/Master/texmf-dist/tex/context/base/mkiv/typo-wrp.mkiv index 4b18785bd33..08134905021 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/typo-wrp.mkiv +++ b/Master/texmf-dist/tex/context/base/mkiv/typo-wrp.mkiv @@ -44,6 +44,7 @@ \spac_crlf_placeholder \ifcase\raggedstatus\hfil\or\or\or\hfil\fi \break + \hskip\zeropoint % new so that the next word also hyphenates \ignorespaces} \unexpanded\def\spac_crlf_placeholder -- cgit v1.2.3