diff options
-rw-r--r-- | README | 1 | ||||
-rw-r--r-- | luaotfload.dtx | 20 | ||||
-rw-r--r-- | otfl-font-cid.lua | 1 | ||||
-rw-r--r-- | otfl-font-def.lua | 27 | ||||
-rw-r--r-- | otfl-font-dum.lua | 4 | ||||
-rw-r--r-- | otfl-font-ini.lua | 5 | ||||
-rw-r--r-- | otfl-font-map.lua | 218 | ||||
-rw-r--r-- | otfl-font-otf.lua | 183 | ||||
-rw-r--r-- | otfl-font-otn.lua | 4 | ||||
-rw-r--r-- | otfl-font-ott.lua | 10 | ||||
-rw-r--r-- | otfl-font-tfm.lua | 19 | ||||
-rw-r--r-- | otfl-font-xtx.lua | 2 | ||||
-rw-r--r-- | otfl-node-dum.lua | 13 | ||||
-rw-r--r-- | otfl-node-ini.lua | 5 | ||||
-rw-r--r-- | otfl-node-inj.lua | 298 | ||||
-rw-r--r-- | otfl-node-res.lua | 14 |
16 files changed, 467 insertions, 357 deletions
@@ -48,6 +48,7 @@ Source files: otfl-font-ott.lua . otfl-font-tfm.lua . otfl-font-xtx.lua . + otfl-font-map.lua . otfl-luat-dum.lua . otfl-node-dum.lua . otfl-node-fnt.lua . diff --git a/luaotfload.dtx b/luaotfload.dtx index 6dceeb8..5f25675 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -162,9 +162,9 @@ and the derived files % algorithm, for instance we can improve the font loading system; this is what % we do in this package. % -%This package is quite low-level, and should be loaded directly in the macro -%package, like it is in Con\TeX t. Sadly, Plain and \LaTeX\ are frozen and -%it's even impossible to adapt them to the new engines. +% This package is quite low-level, and should be loaded directly in the macro +% package, like it is in Con\TeX t. Sadly, Plain and \LaTeX\ are frozen and +% it's even impossible to adapt them to the new engines. % % \subsection{Con\TeX t files needed} % @@ -199,9 +199,18 @@ and the derived files % \item \texttt{font-otc.lua} % \item \texttt{font-def.lua} % \item \texttt{font-xtx.lua} +% \item \texttt{font-map.lua} % \item \texttt{font-dum.lua} % \end{itemize} % +% \subsection{Troubleshooting} +% +% If you encounter problems with some fonts, please first update to the latest version of this package before reporting a bug, as this package is under active development. +% +% A very common problem is the lack of features for some otf fonts even when specified. It can be related to the fact that some fonts do not provide features for the |dflt| script, which is the default one in this package, so you may have to specify the script in the command line, for example: +% +% |\font\myfont = MyFont.otf:script=latn;+liga;| +% % \section{\texttt{luaotfload.lua}} % % \iffalse @@ -398,6 +407,7 @@ do end luaotfload.loadmodule('font-xtx.lua') +luaotfload.loadmodule('font-map.lua') luaotfload.loadmodule('font-dum.lua') % \end{macrocode} @@ -415,8 +425,6 @@ fonts.enc.known = {} % \begin{macrocode} function luaotfload.register_callbacks() - callback.add('ligaturing', nodes.simple_font_dummy, 'luaotfload.ligaturing') - callback.add('kerning', nodes.simple_font_dummy, 'luaotfload.kerning') callback.add('pre_linebreak_filter', nodes.simple_font_handler, 'luaotfload.pre_linebreak_filter') callback.add('hpack_filter', nodes.simple_font_handler, 'luaotfload.hpack_filter') callback.reset('define_font') @@ -424,8 +432,6 @@ function luaotfload.register_callbacks() end function luaotfload.unregister_callbacks() - callback.remove('ligaturing', 'luaotfload.ligaturing') - callback.remove('kerning', 'luaotfload.kerning') callback.remove('pre_linebreak_filter', 'luaotfload.pre_linebreak_filter') callback.remove('hpack_filter', 'luaotfload.hpack_filter') callback.remove('define_font', 'luaotfload.define_font') diff --git a/otfl-font-cid.lua b/otfl-font-cid.lua index b8dfc42..13e9cce 100644 --- a/otfl-font-cid.lua +++ b/otfl-font-cid.lua @@ -93,6 +93,7 @@ local function locate(registry,ordering,supplement) logs.report("load otf","using cidmap file %s",filename) end fonts.cid.map[filename] = cidmap + cidmap.usedname = file.basename(filename) return cidmap end end diff --git a/otfl-font-def.lua b/otfl-font-def.lua index eca6311..3301c39 100644 --- a/otfl-font-def.lua +++ b/otfl-font-def.lua @@ -328,22 +328,18 @@ evolved. Each one has its own way of dealing with its format.</p> --ldx]]-- local function check_tfm(specification,fullname) - -- ofm directive blocks local path search unless set - fullname = resolvers.findbinfile(fullname, 'tfm') or "" -- just to be sure - if fullname ~= "" then - specification.filename, specification.format = fullname, "ofm" + -- ofm directive blocks local path search unless set; btw, in context we + -- don't support ofm files anyway as this format is obsolete + local foundname = resolvers.findbinfile(fullname, 'tfm') or "" -- just to be sure + if foundname == "" then + foundname = resolvers.findbinfile(fullname, 'ofm') or "" -- bonus for usage outside context + end + if foundname ~= "" then + specification.filename, specification.format = foundname, "ofm" return tfm.read_from_tfm(specification) end end ---~ local function check_afm(specification,fullname) ---~ fullname = resolvers.findbinfile(fullname, 'afm') or "" -- just to be sure ---~ if fullname ~= "" then ---~ specification.filename, specification.format = fullname, "afm" ---~ return tfm.read_from_afm(specification) ---~ end ---~ end - local function check_afm(specification,fullname) local foundname = resolvers.findbinfile(fullname, 'afm') or "" -- just to be sure if foundname == "" and tfm.auto_afm then @@ -440,9 +436,10 @@ function readers.opentype(specification,suffix,what) end end -function readers.otf(specification) return readers.opentype(specification,"otf","opentype") end -function readers.ttf(specification) return readers.opentype(specification,"ttf","truetype") end -function readers.ttc(specification) return readers.opentype(specification,"ttf","truetype") end -- !! +function readers.otf (specification) return readers.opentype(specification,"otf","opentype") end +function readers.ttf (specification) return readers.opentype(specification,"ttf","truetype") end +function readers.ttc (specification) return readers.opentype(specification,"ttf","truetype") end -- !! +function readers.dfont(specification) return readers.opentype(specification,"ttf","truetype") end -- !! --[[ldx-- <p>We need to check for default features. For this we provide diff --git a/otfl-font-dum.lua b/otfl-font-dum.lua index 5ae53d0..ef267a4 100644 --- a/otfl-font-dum.lua +++ b/otfl-font-dum.lua @@ -60,7 +60,7 @@ function fonts.names.resolve(name,sub) local d = { } for k, v in pairs(data.mapping) do local t = v[1] - if t == "ttf" or t == "otf" or t == "ttc" then + if t == "ttf" or t == "otf" or t == "ttc" or t == "dfont" then d[k] = v end end @@ -73,7 +73,7 @@ function fonts.names.resolve(name,sub) loaded = true end if type(data) == "table" and data.version == 1.08 then - local condensed = string.gsub(name,"[^%a%d]","") + local condensed = string.gsub(string.lower(name),"[^%a%d]","") local found = data.mapping and data.mapping[condensed] if found then local filename, is_sub = found[3], found[4] diff --git a/otfl-font-ini.lua b/otfl-font-ini.lua index 248a2ba..4005726 100644 --- a/otfl-font-ini.lua +++ b/otfl-font-ini.lua @@ -27,6 +27,11 @@ fonts.mode = 'base' fonts.private = 0xF0000 -- 0x10FFFF fonts.verbose = false -- more verbose cache tables +fonts.ids[0] = { -- nullfont + characters = { }, + descriptions = { }, +} + fonts.methods = fonts.methods or { base = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, node = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, diff --git a/otfl-font-map.lua b/otfl-font-map.lua new file mode 100644 index 0000000..c597d48 --- /dev/null +++ b/otfl-font-map.lua @@ -0,0 +1,218 @@ +if not modules then modules = { } end modules ['font-map'] = { + version = 1.001, + comment = "companion to font-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local match, format, find, concat = string.match, string.format, string.find, table.concat + +local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) + +local ctxcatcodes = tex.ctxcatcodes + +--[[ldx-- +<p>Eventually this code will disappear because map files are kind +of obsolete. Some code may move to runtime or auxiliary modules.</p> +<p>The name to unciode related code will stay of course.</p> +--ldx]]-- + +fonts = fonts or { } +fonts.map = fonts.map or { } +fonts.map.data = fonts.map.data or { } +fonts.map.encodings = fonts.map.encodings or { } +fonts.map.done = fonts.map.done or { } +fonts.map.loaded = fonts.map.loaded or { } +fonts.map.direct = fonts.map.direct or { } +fonts.map.line = fonts.map.line or { } + +function fonts.map.line.pdfmapline(tag,str) + return "\\loadmapline[" .. tag .. "][" .. str .. "]" +end + +function fonts.map.line.pdftex(e) -- so far no combination of slant and stretch + if e.name and e.fontfile then + local fullname = e.fullname or "" + if e.slant and e.slant ~= 0 then + if e.encoding then + return fonts.map.line.pdfmapline("=",format('%s %s "%g SlantFont" <%s <%s',e.name,fullname,e.slant,e.encoding,e.fontfile)) + else + return fonts.map.line.pdfmapline("=",format('%s %s "%g SlantFont" <%s',e.name,fullname,e.slant,e.fontfile)) + end + elseif e.stretch and e.stretch ~= 1 and e.stretch ~= 0 then + if e.encoding then + return fonts.map.line.pdfmapline("=",format('%s %s "%g ExtendFont" <%s <%s',e.name,fullname,e.stretch,e.encoding,e.fontfile)) + else + return fonts.map.line.pdfmapline("=",format('%s %s "%g ExtendFont" <%s',e.name,fullname,e.stretch,e.fontfile)) + end + else + if e.encoding then + return fonts.map.line.pdfmapline("=",format('%s %s <%s <%s',e.name,fullname,e.encoding,e.fontfile)) + else + return fonts.map.line.pdfmapline("=",format('%s %s <%s',e.name,fullname,e.fontfile)) + end + end + else + return nil + end +end + +function fonts.map.flush(backend) -- will also erase the accumulated data + local flushline = fonts.map.line[backend or "pdftex"] or fonts.map.line.pdftex + for _, e in pairs(fonts.map.data) do + tex.sprint(ctxcatcodes,flushline(e)) + end + fonts.map.data = { } +end + +fonts.map.line.dvips = fonts.map.line.pdftex +fonts.map.line.dvipdfmx = function() end + +function fonts.map.convert_entries(filename) + if not fonts.map.loaded[filename] then + fonts.map.data, fonts.map.encodings = fonts.map.load_file(filename,fonts.map.data, fonts.map.encodings) + fonts.map.loaded[filename] = true + end +end + +function fonts.map.load_file(filename, entries, encodings) + entries = entries or { } + encodings = encodings or { } + local f = io.open(filename) + if f then + local data = f:read("*a") + if data then + for line in gmatch(data,"(.-)[\n\t]") do + if find(line,"^[%#%%%s]") then + -- print(line) + else + local stretch, slant, name, fullname, fontfile, encoding + line = line:gsub('"(.+)"', function(s) + stretch = find(s,'"([^"]+) ExtendFont"') + slant = find(s,'"([^"]+) SlantFont"') + return "" + end) + if not name then + -- name fullname encoding fontfile + name, fullname, encoding, fontfile = match(line,"^(%S+)%s+(%S*)[%s<]+(%S*)[%s<]+(%S*)%s*$") + end + if not name then + -- name fullname (flag) fontfile encoding + name, fullname, fontfile, encoding = match(line,"^(%S+)%s+(%S*)[%d%s<]+(%S*)[%s<]+(%S*)%s*$") + end + if not name then + -- name fontfile + name, fontfile = match(line,"^(%S+)%s+[%d%s<]+(%S*)%s*$") + end + if name then + if encoding == "" then encoding = nil end + entries[name] = { + name = name, -- handy + fullname = fullname, + encoding = encoding, + fontfile = fontfile, + slant = tonumber(slant), + stretch = tonumber(stretch) + } + encodings[name] = encoding + elseif line ~= "" then + -- print(line) + end + end + end + end + f:close() + end + return entries, encodings +end + +function fonts.map.load_lum_table(filename) + local lumname = file.replacesuffix(file.basename(filename),"lum") + local lumfile = resolvers.find_file(lumname,"map") or "" + if lumfile ~= "" and lfs.isfile(lumfile) then + if trace_loading or trace_unimapping then + logs.report("load otf","enhance: loading %s ",lumfile) + end + lumunic = dofile(lumfile) + return lumunic, lumfile + end +end + +local hex = lpeg.R("AF","09") +local hexfour = (hex*hex*hex*hex) / function(s) return tonumber(s,16) end +local dec = (lpeg.R("09")^1) / tonumber +local period = lpeg.P(".") + +local unicode = lpeg.P("uni") * (hexfour * (period + lpeg.P(-1)) * lpeg.Cc(false) + lpeg.Ct(hexfour^1) * lpeg.Cc(true)) +local index = lpeg.P("index") * dec * lpeg.Cc(false) + +local parser = unicode + index + +local parsers = { } + +function fonts.map.make_name_parser(str) + if not str or str == "" then + return parser + else + local p = parsers[str] + if not p then + p = lpeg.P(str) * period * dec * lpeg.Cc(false) + parsers[str] = p + end + return p + end +end + +--~ local parser = fonts.map.make_name_parser("Japan1") +--~ local function test(str) +--~ local b, a = parser:match(str) +--~ print((a and table.serialize(b)) or b) +--~ end +--~ test("uni1234") +--~ test("uni1234.xx") +--~ test("uni12349876") +--~ test("index1234") +--~ test("Japan1.123") + +function fonts.map.tounicode16(unicode) + if unicode < 0x10000 then + return format("%04X",unicode) + else + return format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) + end +end + +function fonts.map.tounicode16sequence(unicodes) + local t = { } + for l=1,#unicodes do + local unicode = unicodes[l] + if unicode < 0x10000 then + t[l] = format("%04X",unicode) + else + t[l] = format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) + end + end + return concat(t) +end + +--~ This is quite a bit faster but at the cost of some memory but if we +--~ do this we will also use it elsewhere so let's not follow this route +--~ now. I might use this method in the plain variant (no caching there) +--~ but then I need a flag that distinguishes between code branches. +--~ +--~ local cache = { } +--~ +--~ function fonts.map.tounicode16(unicode) +--~ local s = cache[unicode] +--~ if not s then +--~ if unicode < 0x10000 then +--~ s = format("%04X",unicode) +--~ else +--~ s = format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) +--~ end +--~ cache[unicode] = s +--~ end +--~ return s +--~ end + diff --git a/otfl-font-otf.lua b/otfl-font-otf.lua index 00ac596..f3b3f54 100644 --- a/otfl-font-otf.lua +++ b/otfl-font-otf.lua @@ -12,12 +12,13 @@ local concat, getn, utfbyte = table.concat, table.getn, utf.byte local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip local type, next, tonumber, tostring = type, next, tonumber, tostring -local trace_private = false trackers.register("otf.private", function(v) trace_private = v end) -local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) -local trace_features = false trackers.register("otf.features", function(v) trace_features = v end) -local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) -local trace_sequences = false trackers.register("otf.sequences", function(v) trace_sequences = v end) -local trace_math = false trackers.register("otf.math", function(v) trace_math = v end) +local trace_private = false trackers.register("otf.private", function(v) trace_private = v end) +local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) +local trace_features = false trackers.register("otf.features", function(v) trace_features = v end) +local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) +local trace_sequences = false trackers.register("otf.sequences", function(v) trace_sequences = v end) +local trace_math = false trackers.register("otf.math", function(v) trace_math = v end) +local trace_unimapping = false trackers.register("otf.unimapping", function(v) trace_unimapping = v end) --~ trackers.enable("otf.loading") @@ -81,7 +82,7 @@ otf.features.default = otf.features.default or { } otf.enhancers = otf.enhancers or { } otf.glists = { "gsub", "gpos" } -otf.version = 2.626 -- beware: also sync font-mis.lua +otf.version = 2.628 -- beware: also sync font-mis.lua otf.pack = true -- beware: also sync font-mis.lua otf.syncspace = true otf.notdef = false @@ -464,15 +465,18 @@ otf.enhancers["analyse marks"] = function(data,filename) end end -local other = lpeg.C((1 - lpeg.S("_."))^0) -local ligsplitter = lpeg.Ct(other * (lpeg.P("_") * other)^0) +local separator = lpeg.S("_.") +local other = lpeg.C((1 - separator)^1) +local ligsplitter = lpeg.Ct(other * (separator * other)^0) ---~ print(splitter:match("this")) ---~ print(splitter:match("this.that")) ---~ print(splitter:match("such_so_more")) ---~ print(splitter:match("such_so_more.that")) +--~ print(table.serialize(ligsplitter:match("this"))) +--~ print(table.serialize(ligsplitter:match("this.that"))) +--~ print(table.serialize(ligsplitter:match("japan1.123"))) +--~ print(table.serialize(ligsplitter:match("such_so_more"))) +--~ print(table.serialize(ligsplitter:match("such_so_more.that"))) otf.enhancers["analyse unicodes"] = function(data,filename) + local tounicode16, tounicode16sequence = fonts.map.tounicode16, fonts.map.tounicode16sequence local unicodes = data.luatex.unicodes -- we need to move this code unicodes['space'] = unicodes['space'] or 32 -- handly later on @@ -482,53 +486,113 @@ otf.enhancers["analyse unicodes"] = function(data,filename) -- the tounicode mapping is sparse and only needed for alternatives local tounicode, originals, ns, nl, private, unknown = { }, { }, 0, 0, fonts.private, format("%04X",utfbyte("?")) data.luatex.tounicode, data.luatex.originals = tounicode, originals + local lumunic, uparser, oparser + if false then -- will become an option + lumunic = fonts.map.load_lum_table(filename) + lumunic = lumunic and lumunic.tounicode + end + local cidinfo, cidnames, cidcodes = data.cidinfo + local usedmap = cidinfo and cidinfo.usedname + usedmap = usedmap and fonts.cid.map[usedmap] + if usedmap then + oparser = usedmap and fonts.map.make_name_parser(cidinfo.ordering) + cidnames = usedmap.names + cidcodes = usedmap.unicodes + end + uparser = fonts.map.make_name_parser() for index, glyph in next, data.glyphs do local name, unic = glyph.name, glyph.unicode or -1 -- play safe if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then - -- a.whatever or a_b_c.whatever or a_b_c - local split = ligsplitter:match(name) - if #split == 0 then - -- skip - elseif #split == 1 then - local u = unicodes[split[1]] - if u then - if type(u) == "table" then - u = u[1] - end - if u < 0x10000 then - originals[index], tounicode[index] = u, format("%04X",u) - else - originals[index], tounicode[index] = u, format("%04X%04X",u/1024+0xD800,u%1024+0xDC00) + local unicode = lumunic and lumunic[name] + if unicode then + originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 + end + -- cidmap heuristics, beware, there is no guarantee for a match unless + -- the chain resolves + if not unicode and usedmap then + local foundindex = oparser:match(name) + if foundindex then + unicode = cidcodes[foundindex] -- name to number + if not unicode then + local reference = cidnames[foundindex] -- number to name + if reference then + local foundindex = oparser:match(reference) + if foundindex then + unicode = cidcodes[foundindex] + if unicode then + originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 + end + end + if not unicode then + local foundcodes, multiple = uparser:match(reference) + if foundcodes then + if multiple then + originals[index], tounicode[index], nl, unicode = foundcodes, tounicode16sequence(foundcodes), nl + 1, true + else + originals[index], tounicode[index], ns, unicode = foundcodes, tounicode16(foundcodes), ns + 1, foundcodes + end + end + end + end end - ns = ns + 1 - else - originals[index], tounicode[index] = 0xFFFD, "FFFD" end - else - local as = { } - for l=1,#split do - local u = unicodes[split[l]] - if not u then - as[l], split[l] = 0xFFFD, "FFFD" - else - if type(u) == "table" then - u = u[1] + end + -- a.whatever or a_b_c.whatever or a_b_c (no numbers) + if not unicode then + local split = ligsplitter:match(name) + local nplit = (split and #split) or 0 + if nplit == 0 then + -- skip + elseif nplit == 1 then + unicode = unicodes[split[1]] + if unicode then + if type(unicode) == "table" then + unicode = unicode[1] end - if u < 0x10000 then - as[l], split[l] = u, format("%04X",u) + originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 + end + else + local done = true + for l=1,nplit do + local u = unicodes[split[l]] + if not u then + done = false + break + elseif type(u) == "table" then + split[l] = u[1] else - as[l], split[l] = u, format("%04X%04X",u/1024+0xD800,u%1024+0xDC00) + split[l] = u end end + if done then + originals[index], tounicode[index], nl, unicode = split, tounicode16sequence(split), nl + 1, true + end end - split = concat(split) - if split ~= "" then - originals[index], tounicode[index] = as, split - nl = nl + 1 - else - originals[index], tounicode[index] = 0xFFFD, "FFFD" + end + -- last resort + if not unicode then + local foundcodes, multiple = uparser:match(name) + if foundcodes then + if multiple then + originals[index], tounicode[index], nl, unicode = foundcodes, tounicode16sequence(foundcodes), nl + 1, true + else + originals[index], tounicode[index], ns, unicode = foundcodes, tounicode16(foundcodes), ns + 1, foundcodes + end end end + if not unicode then + originals[index], tounicode[index] = 0xFFFD, "FFFD" + end + end + end + if trace_unimapping then + for index, glyph in table.sortedpairs(data.glyphs) do + local toun, name, unic = tounicode[index], glyph.name, glyph.unicode or -1 -- play safe + if toun then + logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun) + else + logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic) + end end end if trace_loading and (ns > 0 or nl > 0) then @@ -546,16 +610,14 @@ otf.enhancers["analyse subtables"] = function(data,filename) for _, g in next, { data.gsub, data.gpos } do for k=1,#g do local gk = g[k] - -local typ = gk.type -if typ == "gsub_contextchain" or typ == "gpos_contextchain" then - gk.chain = 1 -elseif typ == "gsub_reversecontextchain" or typ == "gpos_reversecontextchain" then - gk.chain = -1 -else - gk.chain = 0 -end - + local typ = gk.type + if typ == "gsub_contextchain" or typ == "gpos_contextchain" then + gk.chain = 1 + elseif typ == "gsub_reversecontextchain" or typ == "gpos_reversecontextchain" then + gk.chain = -1 + else + gk.chain = 0 + end local features = gk.features if features then sequences[#sequences+1] = gk @@ -610,8 +672,9 @@ otf.enhancers["merge cid fonts"] = function(data,filename) local cidinfo = data.cidinfo local verbose = fonts.verbose if cidinfo.registry then - local cidmap = fonts.cid.getmap and fonts.cid.getmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) + local cidmap, cidname = fonts.cid.getmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) if cidmap then + cidinfo.usedname = cidmap.usedname local glyphs, uni_to_int, int_to_uni, nofnames, nofunicodes = { }, { }, { }, 0, 0 local unicodes, names = cidmap.unicodes, cidmap.names for n, subfont in next, data.subfonts do @@ -1343,7 +1406,7 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th } -- indices maps from unicodes to indices for u, i in next, indices do - characters[u] = { } -- we need this because for instance we add protruding info + characters[u] = { } -- we need this because for instance we add protruding info and loop over characters descriptions[u] = glyphs[i] end -- math diff --git a/otfl-font-otn.lua b/otfl-font-otn.lua index 719c0ca..3c11b84 100644 --- a/otfl-font-otn.lua +++ b/otfl-font-otn.lua @@ -150,7 +150,7 @@ trackers.register("*otf.sample","otf.steps,otf.actions,otf.analyzing") local insert_node_after = node.insert_after local delete_node = nodes.delete local copy_node = node.copy -local slide_node_list = node.slide +local find_node_tail = node.tail or node.slide local set_attribute = node.set_attribute local has_attribute = node.has_attribute @@ -1905,7 +1905,7 @@ function fonts.methods.node.otf.features(head,font,attr) local handler = handlers[typ] local thecache = featuredata[typ] or { } -- we need to get rid of this slide ! - start = slide_node_list(head) -- slow (we can store tail because there's always a skip at the end): todo + start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo while start do local id = start.id if id == glyph then diff --git a/otfl-font-ott.lua b/otfl-font-ott.lua index 6676ff6..47c2e0e 100644 --- a/otfl-font-ott.lua +++ b/otfl-font-ott.lua @@ -671,6 +671,16 @@ local to_scripts = otf.tables.to_scripts local to_languages = otf.tables.to_languages local to_features = otf.tables.to_features +for k, v in pairs(to_features) do + local stripped = gsub(k,"%-"," ") + to_features[stripped] = v + local stripped = gsub(k,"[^a-zA-Z0-9]","") + to_features[stripped] = v +end +for k, v in pairs(to_features) do + to_features[lower(k)] = v +end + function otf.meanings.normalize(features) local h = { } for k,v in next, features do diff --git a/otfl-font-tfm.lua b/otfl-font-tfm.lua index 472f69c..8b799df 100644 --- a/otfl-font-tfm.lua +++ b/otfl-font-tfm.lua @@ -245,7 +245,7 @@ function tfm.do_scale(tfmtable, scaledpoints) end -- status local isvirtual = tfmtable.type == "virtual" or tfmtable.virtualized - local hasmath = tfmtable.math_parameters ~= nil or tfmtable.MathConstants ~= nil + local hasmath = (tfmtable.math_parameters ~= nil and next(tfmtable.math_parameters) ~= nil) or (tfmtable.MathConstants ~= nil and next(tfmtable.MathConstants) ~= nil) local nodemode = tfmtable.mode == "node" local hasquality = tfmtable.auto_expand or tfmtable.auto_protrude local hasitalic = tfmtable.has_italic @@ -293,6 +293,7 @@ function tfm.do_scale(tfmtable, scaledpoints) local scaledheight = defaultheight * delta local scaleddepth = defaultdepth * delta local stackmath = tfmtable.ignore_stack_math ~= true +local private = fonts.private for k,v in next, characters do local chr, description, index if ischanged then @@ -355,7 +356,7 @@ function tfm.do_scale(tfmtable, scaledpoints) -- logs.report("define font","t=%s, u=%s, i=%s, n=%s c=%s",k,chr.tounicode or k,description.index,description.name or '-',description.class or '-') -- end if tounicode then - local tu = tounicode[index] + local tu = tounicode[index] -- nb: index! if tu then chr.tounicode = tu end @@ -528,6 +529,7 @@ function tfm.do_scale(tfmtable, scaledpoints) end -- needed for \high cum suis local tpx = tp.x_height +if hasmath then if not tp[13] then tp[13] = .86*tpx end -- mathsupdisplay if not tp[14] then tp[14] = .86*tpx end -- mathsupnormal if not tp[15] then tp[15] = .86*tpx end -- mathsupcramped @@ -535,11 +537,22 @@ function tfm.do_scale(tfmtable, scaledpoints) if not tp[17] then tp[17] = .48*tpx end -- mathsubcombined if not tp[22] then tp[22] = 0 end -- mathaxisheight if t.MathConstants then t.MathConstants.AccentBaseHeight = nil end -- safeguard +end t.tounicode = 1 t.cidinfo = tfmtable.cidinfo -- we have t.name=metricfile and t.fullname=RealName and t.filename=diskfilename -- when collapsing fonts, luatex looks as both t.name and t.fullname as ttc files -- can have multiple subfonts + if hasmath then + if trace_defining then + logs.report("define font","math enabled for: %s %s %s",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") + end + else + if trace_defining then + logs.report("define font","math disabled for: %s %s %s",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") + end + t.nomath, t.MathConstants = true, nil + end return t, delta end @@ -714,7 +727,7 @@ function tfm.set_features(tfmdata) local value = features[f] if value and fi.tfm[f] then -- brr if tfm.trace_features then - logs.report("define tfm","initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown',tfmdata.name or 'unknown') + logs.report("define font","initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown',tfmdata.name or 'unknown') end fi.tfm[f](tfmdata,value) mode = tfmdata.mode or fonts.mode diff --git a/otfl-font-xtx.lua b/otfl-font-xtx.lua index 63e421f..7b3f1ec 100644 --- a/otfl-font-xtx.lua +++ b/otfl-font-xtx.lua @@ -72,7 +72,7 @@ local function isfalse(s) list[s] = 'no' end local function iskey (k,v) list[k] = v end local spaces = lpeg.P(" ")^0 -local namespec = (1-lpeg.S("/: ("))^0 +local namespec = (1-lpeg.S("/:("))^0 -- was: (1-lpeg.S("/: ("))^0 local crapspec = spaces * lpeg.P("/") * (((1-lpeg.P(":"))^0)/iscrap) * spaces local filename = (lpeg.P("file:")/isfile * (namespec/thename)) + (lpeg.P("[") * lpeg.P(true)/isname * (((1-lpeg.P("]"))^0)/thename) * lpeg.P("]")) local fontname = (lpeg.P("name:")/isname * (namespec/thename)) + lpeg.P(true)/issome * (namespec/thename) diff --git a/otfl-node-dum.lua b/otfl-node-dum.lua index 274e0cd..f39a087 100644 --- a/otfl-node-dum.lua +++ b/otfl-node-dum.lua @@ -8,17 +8,12 @@ if not modules then modules = { } end modules ['node-dum'] = { nodes = nodes or { } -function nodes.simple_font_dummy(head,tail) - return tail -end - function nodes.simple_font_handler(head) - local tail = node.slide(head) --- lang.hyphenate(head,tail) - head = nodes.process_characters(head,tail) +-- lang.hyphenate(head) + head = nodes.process_characters(head) nodes.inject_kerns(head) nodes.protect_glyphs(head) - tail = node.ligaturing(head,tail) - tail = node.kerning(head,tail) + head = node.ligaturing(head) + head = node.kerning(head) return head end diff --git a/otfl-node-ini.lua b/otfl-node-ini.lua index 8185e30..3ff73c6 100644 --- a/otfl-node-ini.lua +++ b/otfl-node-ini.lua @@ -110,7 +110,6 @@ local whatsit = node.id('whatsit') local traverse_id = node.traverse_id local traverse = node.traverse -local slide_nodes = node.slide local free_node = node.free local remove_node = node.remove @@ -132,10 +131,10 @@ function nodes.delete(head,current) return nodes.remove(head,current,true) end -nodes.before = node.insert_before -- broken +nodes.before = node.insert_before nodes.after = node.insert_after --- we need to test this, as it might be fixed +-- we need to test this, as it might be fixed now function nodes.before(h,c,n) if c then diff --git a/otfl-node-inj.lua b/otfl-node-inj.lua index 2e8b5ae..dc676a4 100644 --- a/otfl-node-inj.lua +++ b/otfl-node-inj.lua @@ -114,7 +114,7 @@ end function nodes.trace_injection(head) local function dir(n) - return (n<0 and "r-to-l") or (n>0 and "l-to-r") or ("unset") + return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or ("unset") end local function report(...) logs.report("nodes finisher",...) @@ -132,9 +132,9 @@ function nodes.trace_injection(head) if kp then local k = kerns[kp] if k[3] then - report(" pairkern: dir=%s, x=%s, y=%s, w=%s, h=%s",dir(k[1]),k[2],k[3],k[4],k[5]) + report(" pairkern: dir=%s, x=%s, y=%s, w=%s, h=%s",dir(k[1]),k[2] or "?",k[3] or "?",k[4] or "?",k[5] or "?") else - report(" kern: dir=%s, dx=%s",dir(k[1]),k[2]) + report(" kern: dir=%s, dx=%s",dir(k[1]),k[2] or "?") end end if mb then @@ -145,13 +145,13 @@ function nodes.trace_injection(head) if mb then local m = m[mb] if m then - report(" markmark: bound=%s, index=%s, dx=%s, dy=%s",mm,j,m[1],m[2]) + report(" markmark: bound=%s, index=%s, dx=%s, dy=%s",mm,md or "?",m[1] or "?",m[2] or "?") else report(" markmark: bound=%s, missing index",mm) end else m = m[1] - report(" markmark: bound=%s, dx=%s, dy=%s",mm,m[1],m[2]) + report(" markmark: bound=%s, dx=%s, dy=%s",mm,m[1] or "?",m[2] or "?") end end if cb then @@ -159,7 +159,7 @@ function nodes.trace_injection(head) end if cc then local c = cursives[cc] - report(" curscurs: bound=%s, dir=%s, dx=%s, dy=%s",cc,dir(c[1]),c[2],c[3]) + report(" curscurs: bound=%s, dir=%s, dx=%s, dy=%s",cc,dir(c[1]),c[2] or "?",c[3] or "?") end end end @@ -168,18 +168,24 @@ end -- todo: reuse tables (i.e. no collection), but will be extra fields anyway -function nodes.inject_kerns(head,tail,where,keep) - if trace_injections then - nodes.trace_injection(head) - end +function nodes.inject_kerns(head,where,keep) local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns) if has_marks or has_cursives then + if trace_injections then + nodes.trace_injection(head) + end -- in the future variant we will not copy items but refs to tables - local done, ky, rl, valid, cx, wx = false, { }, { }, { }, { }, { } + local done, ky, rl, valid, cx, wx, mk = false, { }, { }, { }, { }, { }, { } if has_kerns then -- move outside loop + local nf, tm = nil, nil for n in traverse_id(glyph,head) do if n.subtype < 256 then valid[#valid+1] = n + if n.font ~= nf then + nf = n.font + tm = fontdata[nf].marks + end + mk[n] = tm[n.char] local k = has_attribute(n,kernpair) if k then local kk = kerns[k] @@ -198,9 +204,15 @@ function nodes.inject_kerns(head,tail,where,keep) end end else + local nf, tm = nil, nil for n in traverse_id(glyph,head) do if n.subtype < 256 then valid[#valid+1] = n + if n.font ~= nf then + nf = n.font + tm = fontdata[nf].marks + end + mk[n] = tm[n.char] end end end @@ -214,22 +226,15 @@ function nodes.inject_kerns(head,tail,where,keep) end -- todo: reuse t and use maxt if has_cursives then - local n_cursbase, n_curscurs, p_cursbase, n, p, nf, tm = nil, nil, nil, nil, nil, nil, nil + local p_cursbase, p = nil, nil -- since we need valid[n+1] we can also use a "while true do" local t, d, maxt = { }, { }, 0 for i=1,#valid do -- valid == glyphs - n = valid[i] - if n.font ~= nf then - nf = n.font ---~ print(n.font,nf,fontdata[nf]) - tm = fontdata[nf].marks - -- maybe flush - maxt = 0 - end - if not tm[n.char] then - n_cursbase = has_attribute(n,cursbase) - n_curscurs = has_attribute(n,curscurs) + local n = valid[i] + if not mk[n] then + local n_cursbase = has_attribute(n,cursbase) if p_cursbase then + local n_curscurs = has_attribute(n,curscurs) if p_cursbase == n_curscurs then local c = cursives[n_curscurs] if c then @@ -257,7 +262,8 @@ function nodes.inject_kerns(head,tail,where,keep) local ny = n.yoffset for i=maxt,1,-1 do ny = ny + d[i] - t[i].yoffset = t[i].yoffset + ny + local ti = t[i] + ti.yoffset = ti.yoffset + ny end maxt = 0 end @@ -265,7 +271,8 @@ function nodes.inject_kerns(head,tail,where,keep) local ny = n.yoffset for i=maxt,1,-1 do ny = ny + d[i] - t[i].yoffset = ny + local ti = t[i] + ti.yoffset = ny end maxt = 0 end @@ -276,7 +283,8 @@ function nodes.inject_kerns(head,tail,where,keep) local ny = n.yoffset for i=maxt,1,-1 do ny = ny + d[i] - t[i].yoffset = ny + local ti = t[i] + ti.yoffset = ny end maxt = 0 end @@ -285,14 +293,13 @@ function nodes.inject_kerns(head,tail,where,keep) end end if has_marks then - local p_markbase, n_markmark = nil, nil for i=1,#valid do local p = valid[i] - p_markbase = has_attribute(p,markbase) + local p_markbase = has_attribute(p,markbase) if p_markbase then local mrks = marks[p_markbase] for n in traverse_id(glyph,p.next) do - n_markmark = has_attribute(n,markmark) + local n_markmark = has_attribute(n,markmark) if p_markbase == n_markmark then local index = has_attribute(n,markdone) or 1 local d = mrks[index] @@ -301,9 +308,17 @@ function nodes.inject_kerns(head,tail,where,keep) -- if rlmode and rlmode < 0 then -- n.xoffset = p.xoffset + d[1] -- else - n.xoffset = p.xoffset - d[1] + n.xoffset = p.xoffset - d[1] +--~ local k = wx[p] +--~ if k then +--~ wx[n] = k +--~ end -- end - n.yoffset = p.yoffset + d[2] + if mk[p] then + n.yoffset = p.yoffset + d[2] + else + n.yoffset = n.yoffset + p.yoffset + d[2] + end end else break @@ -358,224 +373,9 @@ function nodes.inject_kerns(head,tail,where,keep) kerns, cursives, marks = { }, { }, { } end elseif has_kerns then - -- we assume done is true because there are kerns - for n in traverse_id(glyph,head) do - local k = has_attribute(n,kernpair) - if k then - local kk = kerns[k] - if kk then - -- only w can be nil, can be sped up when w == nil - local rl, x, y, w = kk[1], kk[2] or 0, kk[3] or 0, kk[4] or 0 - if y ~= 0 then - n.yoffset = y -- todo: h ? - end - local wx = w - x - if rl < 0 then - if wx ~= 0 then - insert_node_before(head,n,newkern(wx)) - end - if x ~= 0 then - insert_node_after (head,n,newkern(x)) - end - else - -- if wx ~= 0 then - -- insert_node_after(head,n,newkern(wx)) - -- end - if x ~= 0 then - insert_node_before(head,n,newkern(x)) - end - end - end - end + if trace_injections then + nodes.trace_injection(head) end - if not keep then - kerns = { } - end - return head, true - end - return head, false -end - --- -- -- KEEP OLD ONE, THE NEXT IS JUST OPTIMIZED -- -- -- - -function nodes.XXXXXXXxinject_kerns(head,tail,keep) - if trace_injections then - nodes.trace_injection(head) - end - local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns) - if has_marks or has_cursives then - -- in the future variant we will not copy items but refs to tables - local done, ky, valid, cx, wx = false, { }, { }, { }, { } - for n in traverse_id(glyph,head) do - if n.subtype < 256 then - valid[#valid+1] = n - if has_kerns then -- move outside loop - local k = has_attribute(n,kernpair) - if k then - local kk = kerns[k] - if kk then - local x, y, w, h = kk[2], kk[3], kk[4], kk[5] - local dy = y - h - if dy ~= 0 then - ky[n] = dy - end - if w ~= 0 or x ~= 0 then - wx[n] = kk - end - end - end - end - end - end - if #valid > 0 then - -- we can assume done == true because we have cursives and marks - local cx = { } - if has_kerns and next(ky) then - for n, k in next, ky do - n.yoffset = k - end - end - -- todo: reuse t and use maxt - if has_cursives then - local n_cursbase, n_curscurs, p_cursbase, n, p, nf, tm = nil, nil, nil, nil, nil, nil, nil - -- since we need valid[n+1] we can also use a "while true do" - local t, d, maxt = { }, { }, 0 - for i=1,#valid do -- valid == glyphs - n = valid[i] - if n.font ~= nf then - nf = n.font - tm = fontdata[nf].marks - -- maybe flush - maxt = 0 - end - if not tm[n.char] then - n_cursbase = has_attribute(n,cursbase) - n_curscurs = has_attribute(n,curscurs) - if p_cursbase then - if p_cursbase == n_curscurs then - local c = cursives[n_curscurs] - if c then - local rlmode, dx, dy, ws, wn = c[1], c[2], c[3], c[4], c[5] - if rlmode >= 0 then - dx = dx - ws - else - dx = dx + wn - end - if dx ~= 0 then -if rlmode < 0 then - cx[n] = -dx -else - cx[n] = dx -end - end - -- if rlmode and rlmode < 0 then - dy = -dy - -- end - maxt = maxt + 1 - t[maxt] = p - d[maxt] = dy - else - maxt = 0 - end - end - elseif maxt > 0 then - local ny = n.yoffset - for i=maxt,1,-1 do - ny = ny + d[i] - t[i].yoffset = t[i].yoffset + ny - end - maxt = 0 - end - if not n_cursbase and maxt > 0 then - local ny = n.yoffset - for i=maxt,1,-1 do - ny = ny + d[i] - t[i].yoffset = ny - end - maxt = 0 - end - p_cursbase, p = n_cursbase, n - end - end - if maxt > 0 then - local ny = n.yoffset - for i=maxt,1,-1 do - ny = ny + d[i] - t[i].yoffset = ny - end - maxt = 0 - end - if not keep then - cursives = { } - end - end - if has_marks then - local p_markbase, n_markmark = nil, nil - for i=1,#valid do - local p = valid[i] - p_markbase = has_attribute(p,markbase) - if p_markbase then - local mrks = marks[p_markbase] - for n in traverse_id(glyph,p.next) do - n_markmark = has_attribute(n,markmark) - if p_markbase == n_markmark then - local index = has_attribute(n,markdone) or 1 - local d = mrks[index] - if d then - local d1, d2 = d[1], d[2] - if d1 ~= 0 then - n.xoffset = p.xoffset - d[1] - end - if d2 ~= 0 then - n.yoffset = p.yoffset + d[2] - end - end - else - break - end - end - end - end - if not keep then - marks = { } - end - end - -- todo : combine - if next(wx) then - for n, k in next, wx do - -- only w can be nil, can be sped up when w == nil - local rl, x, w = k[1], k[2] or 0, k[4] or 0 - local wx = w - x - if rl < 0 then - if wx ~= 0 then - insert_node_before(head,n,newkern(wx)) - end - if x ~= 0 then - insert_node_after (head,n,newkern(x)) - end - else - -- if wx ~= 0 then - -- insert_node_after(head,n,newkern(wx)) - -- end - if x ~= 0 then - insert_node_before(head,n,newkern(x)) - end - end - end - end - if next(cx) then - for n, k in next, cx do - insert_node_before(head,n,newkern(k)) - end - end - if not keep then - kerns = { } - end - return head, true - elseif not keep then - kerns, cursives, marks = { }, { }, { } - end - elseif has_kerns then -- we assume done is true because there are kerns for n in traverse_id(glyph,head) do local k = has_attribute(n,kernpair) @@ -610,6 +410,8 @@ end kerns = { } end return head, true + else + -- no tracing needed end return head, false end diff --git a/otfl-node-res.lua b/otfl-node-res.lua index b84e5b1..4f2cf5a 100644 --- a/otfl-node-res.lua +++ b/otfl-node-res.lua @@ -7,7 +7,7 @@ if not modules then modules = { } end modules ['node-res'] = { } local gmatch, format = string.gmatch, string.format -local copy_node, free_node, new_node = node.copy, node.free, node.new +local copy_node, free_node, free_list, new_node = node.copy, node.free, node.flush_list, node.new --[[ldx-- <p>The next function is not that much needed but in <l n='context'/> we use @@ -51,7 +51,6 @@ function nodes.usage() return t end -local pdfliteral = nodes.register(new_node("whatsit",8)) pdfliteral.mode = 1 local disc = nodes.register(new_node("disc")) local kern = nodes.register(new_node("kern",1)) local penalty = nodes.register(new_node("penalty")) @@ -60,6 +59,7 @@ local glue_spec = nodes.register(new_node("glue_spec")) local glyph = nodes.register(new_node("glyph",0)) local textdir = nodes.register(new_node("whatsit",7)) local rule = nodes.register(new_node("rule")) +local latelua = nodes.register(new_node("whatsit",35)) function nodes.glyph(fnt,chr) local n = copy_node(glyph) @@ -91,11 +91,6 @@ end function nodes.disc() return copy_node(disc) end -function nodes.pdfliteral(str) - local t = copy_node(pdfliteral) - t.data = str - return t -end function nodes.textdir(dir) local t = copy_node(textdir) t.dir = dir @@ -108,6 +103,11 @@ function nodes.rule(w,h,d) if d then n.depth = d end return n end +function nodes.latelua(code) + local n = copy_node(latelua) + n.data = code + return n +end statistics.register("cleaned up reserved nodes", function() return format("%s nodes, %s lists of %s", nodes.cleanup_reserved(tex.count["lastallocatedbox"])) |