From 50476780411fb84f1f4d0dcb7cd865c1eba4141b Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 20 Aug 2016 21:31:35 +0200 Subject: [fontloader] sync with Context as of 2016-08-29 --- src/fontloader/misc/fontloader-data-con.lua | 2 +- src/fontloader/misc/fontloader-font-cff.lua | 231 ++++-- src/fontloader/misc/fontloader-font-dsp.lua | 3 +- src/fontloader/misc/fontloader-font-onr.lua | 197 +++-- src/fontloader/misc/fontloader-font-osd.lua | 156 ++-- src/fontloader/misc/fontloader-font-ota.lua | 54 +- src/fontloader/misc/fontloader-font-otj.lua | 42 +- src/fontloader/misc/fontloader-font-otl.lua | 2 +- src/fontloader/misc/fontloader-font-otr.lua | 181 +---- src/fontloader/misc/fontloader-font-ots.lua | 68 +- src/fontloader/misc/fontloader-l-table.lua | 15 +- src/fontloader/misc/fontloader-util-str.lua | 88 ++- src/fontloader/runtime/fontloader-basics-gen.lua | 100 ++- src/fontloader/runtime/fontloader-reference.lua | 915 ++++++++++++----------- 14 files changed, 1145 insertions(+), 909 deletions(-) diff --git a/src/fontloader/misc/fontloader-data-con.lua b/src/fontloader/misc/fontloader-data-con.lua index 240538d..c79fca7 100644 --- a/src/fontloader/misc/fontloader-data-con.lua +++ b/src/fontloader/misc/fontloader-data-con.lua @@ -91,7 +91,7 @@ function containers.read(container,name) local storage = container.storage local stored = storage[name] if not stored and container.enabled and caches and containers.usecache then - stored = caches.loaddata(container.readables,name) + stored = caches.loaddata(container.readables,name,container.writable) if stored and stored.cache_version == container.version then if trace_cache or trace_containers then report_containers("action %a, category %a, name %a","load",container.subcategory,name) diff --git a/src/fontloader/misc/fontloader-font-cff.lua b/src/fontloader/misc/fontloader-font-cff.lua index 8c57b47..1c6bd56 100644 --- a/src/fontloader/misc/fontloader-font-cff.lua +++ b/src/fontloader/misc/fontloader-font-cff.lua @@ -26,6 +26,7 @@ local concat, remove = table.concat, table.remove local floor, abs, round, ceil = math.floor, math.abs, math.round, math.ceil local P, C, R, S, C, Cs, Ct = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Ct local lpegmatch = lpeg.match +local formatters = string.formatters local readers = fonts.handlers.otf.readers local streamreader = readers.streamreader @@ -594,6 +595,7 @@ do local ymax = 0 local checked = false local keepcurve = false + local version = 2 local function showstate(where) report("%w%-10s : [%s] n=%i",depth*2,where,concat(stack," ",1,top),top) @@ -1052,13 +1054,114 @@ do end end - local function unsupported() + local function unsupported(t) if trace_charstrings then - showstate("unsupported") + showstate("unsupported " .. t) end top = 0 end + local function unsupportedsub(t) + if trace_charstrings then + showstate("unsupported sub " .. t) + end + top = 0 + end + + -- type 1 (not used in type 2) + + local function getstem3() + if trace_charstrings then + showstate("stem3") + end + top = 0 + end + + local function divide() + if version == 1 then + local d = stack[top] + top = top - 1 + stack[top] = stack[top] / d + end + end + + local function closepath() + if version == 1 then + if trace_charstrings then + showstate("closepath") + end + end + top = 0 + end + + local function hsbw() + if version == 1 then + if trace_charstrings then + showstate("dotsection") + end + width = stack[top] + end + top = 0 + end + + local function seac() + if version == 1 then + if trace_charstrings then + showstate("seac") + end + end + top = 0 + end + + local function sbw() + if version == 1 then + if trace_charstrings then + showstate("sbw") + end + width = stack[top-1] + end + top = 0 + end + + -- these are probably used for special cases i.e. call out to postscript + + local function callothersubr() + if version == 1 then + -- we don't support this (ok, we could mimick these othersubs) + if trace_charstrings then + showstate("callothersubr (unsupported)") + end + end + top = 0 + end + + local function pop() + if version == 1 then + -- we don't support this + if trace_charstrings then + showstate("pop (unsupported)") + end + top = top + 1 + stack[top] = 0 -- a dummy + else + top = 0 + end + end + + local function setcurrentpoint() + if version == 1 then + -- we don't support this + if trace_charstrings then + showstate("pop (unsupported)") + end + x = x + stack[top-1] + y = y + stack[top] + end + top = 0 + end + + -- so far for unsupported postscript + -- Bah, we cannot use a fast lpeg because a hint has an unknown size and a -- runtime capture cannot handle that well. @@ -1076,7 +1179,7 @@ do unsupported, -- 10 -- calllocal, unsupported, -- 11 -- callreturn, unsupported, -- 12 -- elsewhere - unsupported, -- 13 -- hsbw + hsbw, -- 13 -- hsbw (type 1 cff) unsupported, -- 14 -- endchar, unsupported, -- 15 unsupported, -- 16 @@ -1098,6 +1201,17 @@ do } local subactions = { + -- cff 1 + [000] = dotsection, + [001] = getstem3, + [002] = getstem3, + [006] = seac, + [007] = sbw, + [012] = divide, + [016] = callothersubr, + [017] = pop, + [033] = setcurrentpoint, + -- cff 2 [034] = hflex, [035] = flex, [036] = hflex1, @@ -1107,23 +1221,29 @@ do local p_bytes = Ct((P(1)/byte)^0) local function call(scope,list,bias,process) - local index = stack[top] + bias - top = top - 1 - if trace_charstrings then - showvalue(scope,index,true) - end - local str = list[index] - if str then - if type(str) == "string" then - str = lpegmatch(p_bytes,str) - list[index] = str - end - depth = depth + 1 - process(str) - depth = depth - 1 + depth = depth + 1 + if top == 0 then + showstate(formatters["unknown %s call"](scope)) + top = 0 else - report("unknown %s %i",scope,index) + local index = stack[top] + bias + top = top - 1 + if trace_charstrings then + showvalue(scope,index,true) + end + local tab = list[index] + if tab then + if type(tab) == "string" then + tab = lpegmatch(p_bytes,tab) + list[index] = tab + end + process(tab) + else + showstate(formatters["unknown %s call %i"](scope,index)) + top = 0 + end end + depth = depth - 1 end local function process(tab) @@ -1131,7 +1251,7 @@ do local n = #tab while i <= n do local t = tab[i] - if t >= 32 and t<=246 then + if t >= 32 and t <= 246 then -- -107 .. +107 top = top + 1 stack[top] = t - 139 @@ -1196,7 +1316,7 @@ do local t = tab[i] local a = subactions[t] if a then - a() + a(t) else if trace_charstrings then showvalue("",t) @@ -1207,7 +1327,7 @@ do else local a = actions[t] if a then - local s = a() + local s = a(t) if s then i = i + s end @@ -1260,27 +1380,44 @@ do -- end -- end - parsecharstrings = function(data,glyphs,doshapes) + local function setbias(globals,locals) + if version == 1 then + return + false, + false + else + local g, l = #globals, #locals + return + ((g < 1240 and 107) or (g < 33900 and 1131) or 32768) + 1, + ((l < 1240 and 107) or (l < 33900 and 1131) or 32768) + 1 + end + end + + parsecharstrings = function(data,glyphs,doshapes,tversion) -- for all charstrings local dictionary = data.dictionaries[1] local charstrings = dictionary.charstrings local charset = dictionary.charset + local private = dictionary.private or { data = { } } + keepcurve = doshapes + version = tversion stack = { } glyphs = glyphs or { } strings = data.strings - locals = dictionary.subroutines - globals = data.routines - globalbias = #globals - localbias = #locals - globalbias = ((globalbias < 1240 and 107) or (globalbias < 33900 and 1131) or 32768) + 1 - localbias = ((localbias < 1240 and 107) or (localbias < 33900 and 1131) or 32768) + 1 - local nominalwidth = dictionary.private.data.nominalwidthx or 0 - local defaultwidth = dictionary.private.data.defaultwidthx or 0 + globals = data.routines or { } + locals = dictionary.subroutines or { } + + globalbias, localbias = setbias(globals,locals) + + local nominalwidth = private.data.nominalwidthx or 0 + local defaultwidth = private.data.defaultwidthx or 0 for i=1,#charstrings do - local str = charstrings[i] - local tab = lpegmatch(p_bytes,str) + local tab = charstrings[i] + if type(tab) == "string" then + tab = lpegmatch(p_bytes,tab) + end local index = i - 1 x = 0 y = 0 @@ -1341,20 +1478,23 @@ do return glyphs end - parsecharstring = function(data,dictionary,charstring,glyphs,index,doshapes) + parsecharstring = function(data,dictionary,tab,glyphs,index,doshapes,tversion) local private = dictionary.private keepcurve = doshapes + version = tversion strings = data.strings -- or in dict? locals = dictionary.subroutines or { } globals = data.routines or { } - globalbias = #globals - localbias = #locals - globalbias = ((globalbias < 1240 and 107) or (globalbias < 33900 and 1131) or 32768) + 1 - localbias = ((localbias < 1240 and 107) or (localbias < 33900 and 1131) or 32768) + 1 + + globalbias, localbias = setbias(globals,locals) + local nominalwidth = private and private.data.nominalwidthx or 0 local defaultwidth = private and private.data.defaultwidthx or 0 -- - local tab = lpegmatch(p_bytes,charstring) + if type(tab) == "string" then + tab = lpegmatch(p_bytes,tab) + end + -- x = 0 y = 0 width = false @@ -1384,7 +1524,8 @@ do width = nominalwidth + width end -- -index = index - 1 + index = index - 1 + -- local glyph = glyphs[index] -- can be autodefined in otr if not glyph then glyphs[index] = { @@ -1410,8 +1551,6 @@ index = index - 1 report("width: %s",tostring(width)) report("boundingbox: % t",boundingbox) end - -- - return charstring end resetcharstrings = function() @@ -1542,7 +1681,7 @@ local function readcidprivates(f,data) parseprivates(data,dictionaries) end -local function readnoselect(f,data,glyphs,doshapes) +local function readnoselect(f,data,glyphs,doshapes,version) local dictionaries = data.dictionaries local dictionary = dictionaries[1] readglobals(f,data) @@ -1552,11 +1691,13 @@ local function readnoselect(f,data,glyphs,doshapes) readprivates(f,data) parseprivates(data,data.dictionaries) readlocals(f,data,dictionary) - parsecharstrings(data,glyphs,doshapes) + parsecharstrings(data,glyphs,doshapes,version) resetcharstrings() end -local function readfdselect(f,data,glyphs,doshapes) +readers.parsecharstrings = parsecharstrings + +local function readfdselect(f,data,glyphs,doshapes,version) local header = data.header local dictionaries = data.dictionaries local dictionary = dictionaries[1] @@ -1615,7 +1756,7 @@ local function readfdselect(f,data,glyphs,doshapes) readlocals(f,data,dictionaries[i]) end for i=1,#charstrings do - parsecharstring(data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes) + parsecharstring(data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version) end resetcharstrings() end diff --git a/src/fontloader/misc/fontloader-font-dsp.lua b/src/fontloader/misc/fontloader-font-dsp.lua index 49d5929..1c81e5e 100644 --- a/src/fontloader/misc/fontloader-font-dsp.lua +++ b/src/fontloader/misc/fontloader-font-dsp.lua @@ -53,6 +53,7 @@ local bittest = bit32.btest local rshift = bit32.rshift local concat = table.concat local lower = string.lower +local copy = table.copy local sub = string.sub local strip = string.strip local tohash = table.tohash @@ -1698,7 +1699,7 @@ do flags = d.flags, -- chain = d.chain, } - sublookuplist[nofsublookups] = h + sublookuplist[nofsublookups] = copy(h) -- we repack later sublookuphash[lookupid] = nofsublookups sublookupcheck[lookupid] = 1 else diff --git a/src/fontloader/misc/fontloader-font-onr.lua b/src/fontloader/misc/fontloader-font-onr.lua index dcf7445..6c33b24 100644 --- a/src/fontloader/misc/fontloader-font-onr.lua +++ b/src/fontloader/misc/fontloader-font-onr.lua @@ -50,31 +50,41 @@ and reader.

and new vectors (we actually had one bad vector with the old loader).

--ldx]]-- -local get_indexes +local get_indexes, get_shapes do - local n, m + local decrypt - local progress = function(str,position,name,size) - local forward = position + tonumber(size) + 3 + 2 - n = n + 1 - if n >= m then - return #str, name - elseif forward < #str then - return forward, name - else - return #str, name + do + + local r, c1, c2, n = 0, 0, 0, 0 + + local function step(c) + local cipher = byte(c) + local plain = bxor(cipher,rshift(r,8)) + r = ((cipher + r) * c1 + c2) % 65536 + return char(plain) end - end - local initialize = function(str,position,size) - n = 0 - m = size -- % tonumber(size) - return position + 1 + decrypt = function(binary,initial,seed) + r, c1, c2, n = initial, 52845, 22719, seed + binary = gsub(binary,".",step) + return sub(binary,n+1) + end + + -- local pattern = Cs((P(1) / step)^1) + -- + -- decrypt = function(binary,initial,seed) + -- r, c1, c2, n = initial, 52845, 22719, seed + -- binary = lpegmatch(pattern,binary) + -- return sub(binary,n+1) + -- end + end local charstrings = P("/CharStrings") + local subroutines = P("/Subrs") local encoding = P("/Encoding") local dup = P("dup") local put = P("put") @@ -85,9 +95,64 @@ do local spaces = P(" ")^1 local spacing = patterns.whitespace^0 + local routines, vector, chars, n, m + + local initialize = function(str,position,size) + n = 0 + m = size -- % tonumber(size) + return position + 1 + end + + local setroutine = function(str,position,index,size) + local forward = position + tonumber(size) + local stream = sub(str,position+1,forward) + routines[index] = decrypt(stream,4330,4) + return forward + end + + local setvector = function(str,position,name,size) + local forward = position + tonumber(size) + if n >= m then + return #str + elseif forward < #str then + vector[n] = name + n = n + 1 -- we compensate for notdef at the cff loader end + return forward + else + return #str + end + end + + local setshapes = function(str,position,name,size) + local forward = position + tonumber(size) + local stream = sub(str,position+1,forward) + if n > m then + return #str + elseif forward < #str then + vector[n] = name + n = n + 1 + chars [n] = decrypt(stream,4330,4) + return forward + else + return #str + end + end + + local p_rd = spacing * (P("RD") + P("-|")) + local p_np = spacing * (P("NP") + P( "|")) + local p_nd = spacing * (P("ND") + P( "|")) + + local p_filterroutines = -- dup RD or -| NP or | + (1-subroutines)^0 * subroutines * spaces * Cmt(cardinal,initialize) + * (Cmt(cardinal * spaces * cardinal * p_rd, setroutine) * p_np + P(1))^1 + + local p_filtershapes = -- /foo RD ND + (1-charstrings)^0 * charstrings * spaces * Cmt(cardinal,initialize) + * (Cmt(name * spaces * cardinal * p_rd, setshapes) * p_nd + P(1))^1 + local p_filternames = Ct ( (1-charstrings)^0 * charstrings * spaces * Cmt(cardinal,initialize) - * (Cmt(name * spaces * cardinal, progress) + P(1))^1 + * (Cmt(name * spaces * cardinal, setvector) + P(1))^1 ) -- /Encoding 256 array @@ -102,36 +167,7 @@ do -- if one of first 4 not 0-9A-F then binary else hex - local decrypt - - do - - local r, c1, c2, n = 0, 0, 0, 0 - - local function step(c) - local cipher = byte(c) - local plain = bxor(cipher,rshift(r,8)) - r = ((cipher + r) * c1 + c2) % 65536 - return char(plain) - end - - decrypt = function(binary) - r, c1, c2, n = 55665, 52845, 22719, 4 - binary = gsub(binary,".",step) - return sub(binary,n+1) - end - - -- local pattern = Cs((P(1) / step)^1) - -- - -- decrypt = function(binary) - -- r, c1, c2, n = 55665, 52845, 22719, 4 - -- binary = lpegmatch(pattern,binary) - -- return sub(binary,n+1) - -- end - - end - - local function loadpfbvector(filename) + local function loadpfbvector(filename,shapestoo) -- for the moment limited to encoding only local data = io.loaddata(resolvers.findfile(filename)) @@ -153,28 +189,36 @@ do return end - binary = decrypt(binary,4) + binary = decrypt(binary,55665,4) - local vector = lpegmatch(p_filternames,binary) - --- if vector[1] == ".notdef" then --- -- tricky --- vector[0] = table.remove(vector,1) --- end - - for i=1,#vector do - vector[i-1] = vector[i] + local names = { } + local encoding = lpegmatch(p_filterencoding,ascii) + local glyphs = { } + + routines, vector, chars = { }, { }, { } + + if shapestoo then + lpegmatch(p_filterroutines,binary) + lpegmatch(p_filtershapes,binary) + local data = { + dictionaries = { + { + charstrings = chars, + charset = vector, + subroutines = routines, + } + }, + } + fonts.handlers.otf.readers.parsecharstrings(data,glyphs,true,true) + else + lpegmatch(p_filternames,binary) end - vector[#vector] = nil - if not vector then - report_pfb("no vector in %a",filename) - return - end + names = vector - local encoding = lpegmatch(p_filterencoding,ascii) + routines, vector, chars = nil, nil, nil - return vector, encoding + return names, encoding, glyphs end @@ -202,6 +246,11 @@ do end end + get_shapes = function(pfbname) + local vector, encoding, glyphs = loadpfbvector(pfbname,true) + return glyphs + end + end --[[ldx-- @@ -428,6 +477,26 @@ function readers.loadfont(afmname,pfbname) end end +-- for now, todo: n and check with otf (no afm needed here) + +function readers.loadshapes(filename) + local fullname = resolvers.findfile(filename) or "" + if fullname == "" then + return { + filename = "not found: " .. filename, + glyphs = { } + } + else + return { + filename = fullname, + format = "opentype", + glyphs = get_shapes(fullname) or { }, + units = 1000, + } + end +end + + function readers.getinfo(filename) local data = read(resolvers.findfile(filename),infoparser) if data then diff --git a/src/fontloader/misc/fontloader-font-osd.lua b/src/fontloader/misc/fontloader-font-osd.lua index 26af691..b67cc92 100644 --- a/src/fontloader/misc/fontloader-font-osd.lua +++ b/src/fontloader/misc/fontloader-font-osd.lua @@ -613,13 +613,12 @@ local function initializedevanagi(tfmdata) local steps = sequence.steps local nofsteps = sequence.nofsteps local features = sequence.features - if features["rphf"] then - -- deva + local has_rphf = features.rphf + local has_blwf = features.blwf + if has_rphf and has_rphf.deva then devanagari.reph = true - elseif features["blwf"] then - -- deva + elseif has_blwf and has_blwf.deva then devanagari.vattu = true - -- dev2 for i=1,nofsteps do local step = steps[i] local coverage = step.coverage @@ -632,59 +631,71 @@ local function initializedevanagi(tfmdata) end end end - if valid[kind] then - for i=1,nofsteps do - local step = steps[i] - local coverage = step.coverage - if coverage then - local reph = false - if step.osdstep then - -- rphf acts on consonant + halant - for k, v in next, ra do - local r = coverage[k] - if r then - local h = false - for k, v in next, halant do - local h = r[k] - if h then - reph = h.ligature or false - break + for kind, spec in next, features do -- beware, this is + if spec.dev2 and valid[kind] then + for i=1,nofsteps do + local step = steps[i] + local coverage = step.coverage + if coverage then + local reph = false + if kind == "rphf" then + -- + -- KE: I don't understand the rationale behind osdstep. The original if + -- statement checked whether coverage is contextual chaining. + -- + -- HH: The osdstep signals that we deal with our own feature here, not + -- one in the font itself so it was just a safeguard against us overloading + -- something driven by the font. + -- + -- if step.osdstep then -- selective + if true then -- always + -- rphf acts on consonant + halant + for k, v in next, ra do + local r = coverage[k] + if r then + local h = false + for k, v in next, halant do + local h = r[k] + if h then + reph = h.ligature or false + break + end + end + if reph then + break + end end end - if reph then - break - end + else + -- rphf might be result of other handler/chainproc end end - else - -- rphf might be result of other handler/chainproc + seqsubset[#seqsubset+1] = { kind, coverage, reph } end - seqsubset[#seqsubset+1] = { kind, coverage, reph } end end - end - if kind == "pref" then - local sequence = dataset[3] -- was [5] - local steps = sequence.steps - local nofsteps = sequence.nofsteps - for i=1,nofsteps do - local step = steps[i] - local coverage = step.coverage - if coverage then - for k, v in next, halant do - local h = coverage[k] - if h then - local found = false - for k, v in next, h do - found = v and v.ligature + if kind == "pref" then + local steps = sequence.steps + local nofsteps = sequence.nofsteps + for i=1,nofsteps do + local step = steps[i] + local coverage = step.coverage + if coverage then + for k, v in next, halant do + local h = coverage[k] + if h then + local found = false + for k, v in next, h do + found = v and v.ligature + if found then + pre_base_reordering_consonants[k] = found + break + end + end if found then - pre_base_reordering_consonants[k] = found break end end - if found then - break - end end end end @@ -1132,6 +1143,8 @@ function handlers.devanagari_reorder_matras(head,start) -- no leak start = startnext break end + else + break end current = next end @@ -1171,12 +1184,12 @@ function handlers.devanagari_reorder_reph(head,start) local startfont = getfont(start) local startattr = getprop(start,a_syllabe) while current do - local char = ischar(current,font) + local char = ischar(current,startfont) if char and getprop(current,a_syllabe) == startattr then -- step 2 if halant[char] and not getprop(current,a_state) then local next = getnext(current) if next then - local nextchar = ischar(next,font) + local nextchar = ischar(next,startfont) if nextchar and zw_char[nextchar] and getprop(next,a_syllabe) == startattr then current = next next = getnext(current) @@ -1198,7 +1211,7 @@ function handlers.devanagari_reorder_reph(head,start) if not startnext then current = getnext(start) while current do - local char = ischar(current,font) + local char = ischar(current,startfont) if char and getprop(current,a_syllabe) == startattr then -- step 4 if getprop(current,a_state) == s_pstf then -- post-base startnext = getnext(start) @@ -1223,7 +1236,7 @@ function handlers.devanagari_reorder_reph(head,start) current = getnext(start) local c = nil while current do - local char = ischar(current,font) + local char = ischar(current,startfont) if char and getprop(current,a_syllabe) == startattr then -- step 5 if not c and mark_above_below_post[char] and reorder_class[char] ~= "after subscript" then c = current @@ -1250,7 +1263,7 @@ function handlers.devanagari_reorder_reph(head,start) current = start local next = getnext(current) while next do - local nextchar = ischar(next,font) + local nextchar = ischar(next,startfont) if nextchar and getprop(next,a_syllabe) == startattr then --step 6 current = next next = getnext(current) @@ -1263,7 +1276,7 @@ function handlers.devanagari_reorder_reph(head,start) head = remove_node(head,start) local next = getnext(current) setlink(start,next) - setlink(current,"next",start) + setlink(current,start) start = startnext end end @@ -1290,12 +1303,12 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) local startattr = getprop(start,a_syllabe) -- can be fast for loop + caching state while current do - local char = ischar(current,font) + local char = ischar(current,startfont) if char and getprop(current,a_syllabe) == startattr then local next = getnext(current) if halant[char] and not getprop(current,a_state) then if next then - local nextchar = ischar(next,font) + local nextchar = ischar(next,startfont) if nextchar and getprop(next,a_syllabe) == startattr then if nextchar == c_zwnj or nextchar == c_zwj then current = next @@ -1319,13 +1332,13 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) current = getnext(start) startattr = getprop(start,a_syllabe) while current do - local char = ischar(current,font) + local char = ischar(current,startfont) if char and getprop(current,a_syllabe) == startattr then if not consonant[char] and getprop(current,a_state) then -- main startnext = getnext(start) removenode(start,start) local prev = getprev(current) - setlink(start,prev) + setlink(prev,start) setlink(start,current) start = startnext break @@ -1426,21 +1439,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa local kind = subset[1] local lookupcache = subset[2] if kind == "rphf" then - for k, v in next, ra do - local r = lookupcache[k] - if r then - for k, v in next, halant do - local h = r[k] - if h then - reph = h.ligature or false - break - end - end - if reph then - break - end - end - end + reph = subset[3] local current = start local last = getnext(stop) while current ~= last do @@ -1473,7 +1472,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa if current ~= stop then local c = locl[current] or getchar(current) local found = lookupcache[c] - if found then + if found then -- pre-base: pref Halant + Consonant local next = getnext(current) local n = locl[next] or getchar(next) if found[n] then @@ -1718,7 +1717,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa local prev = getprev(current) if prev ~= target then local next = getnext(current) - setlink(next,prev) + setlink(prev,next) if current == stop then stop = prev end @@ -1752,7 +1751,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa stop = current end local prev = getprev(c) - setlink(next,prev) + setlink(prev,next) local nextnext = getnext(next) setnext(current,nextnext) local nextnextnext = getnext(nextnext) @@ -1766,6 +1765,9 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa end if getchar(base) == c_nbsp then + if base == stop then + stop = getprev(stop) + end nbspaces = nbspaces - 1 head = remove_node(head, base) flush_node(base) @@ -1815,7 +1817,7 @@ local function analyze_next_chars_one(c,font,variant) -- skip one dependent vowe elseif (vv == c_zwnj or vv == c_zwj) and halant[vvv] then local nnnn = getnext(nnn) if nnnn then - local vvvv = ischar(nnnn) + local vvvv = ischar(nnnn,font) if vvvv and consonant[vvvv] then c = nnnn end @@ -1838,7 +1840,7 @@ local function analyze_next_chars_one(c,font,variant) -- skip one dependent vowe local nn = getnext(n) if nn then local vv = ischar(nn,font) - if vv and zw_char[vv] then + if vv and zw_char[v] then n = nn v = vv nn = getnext(nn) diff --git a/src/fontloader/misc/fontloader-font-ota.lua b/src/fontloader/misc/fontloader-font-ota.lua index 4ddb831..b8944e0 100644 --- a/src/fontloader/misc/fontloader-font-ota.lua +++ b/src/fontloader/misc/fontloader-font-ota.lua @@ -262,36 +262,44 @@ local classifiers = characters.classifiers if not classifiers then - local first_arabic, last_arabic = characters.blockrange("arabic") - local first_syriac, last_syriac = characters.blockrange("syriac") - local first_mandiac, last_mandiac = characters.blockrange("mandiac") - local first_nko, last_nko = characters.blockrange("nko") + local f_arabic, l_arabic = characters.blockrange("arabic") + local f_syriac, l_syriac = characters.blockrange("syriac") + local f_mandiac, l_mandiac = characters.blockrange("mandiac") + local f_nko, l_nko = characters.blockrange("nko") + local f_ext_a, l_ext_a = characters.blockrange("arabicextendeda") classifiers = table.setmetatableindex(function(t,k) - local c = chardata[k] - local v = false - if c then - local arabic = c.arabic - if arabic then - v = mappers[arabic] - if not v then - log.report("analyze","error in mapping arabic %C",k) - -- error - v = false - end - elseif k >= first_arabic and k <= last_arabic or k >= first_syriac and k <= last_syriac or - k >= first_mandiac and k <= last_mandiac or k >= first_nko and k <= last_nko then - if categories[k] == "mn" then - v = s_mark - else - v = s_rest + if type(k) == "number" then + local c = chardata[k] + local v = false + if c then + local arabic = c.arabic + if arabic then + v = mappers[arabic] + if not v then + log.report("analyze","error in mapping arabic %C",k) + -- error + v = false + end + elseif (k >= f_arabic and k <= l_arabic) or + (k >= f_syriac and k <= l_syriac) or + (k >= f_mandiac and k <= l_mandiac) or + (k >= f_nko and k <= l_nko) or + (k >= f_ext_a and k <= l_ext_a) then + if categories[k] == "mn" then + v = s_mark + else + v = s_rest + end end end + t[k] = v + return v end - t[k] = v - return v end) + characters.classifiers = classifiers + end function methods.arab(head,font,attr) diff --git a/src/fontloader/misc/fontloader-font-otj.lua b/src/fontloader/misc/fontloader-font-otj.lua index 46b2ca8..68cf608 100644 --- a/src/fontloader/misc/fontloader-font-otj.lua +++ b/src/fontloader/misc/fontloader-font-otj.lua @@ -377,7 +377,8 @@ function injections.setkern(current,factor,rlmode,x,injection) end end -function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) -- ba=baseanchor, ma=markanchor +function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmark) -- ba=baseanchor, ma=markanchor + local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) nofregisteredmarks = nofregisteredmarks + 1 if rlmode >= 0 then @@ -398,6 +399,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) -- ba=b i.markbase = nofregisteredmarks i.markbasenode = base i.markmark = mkmk + i.checkmark = checkmark end else p.injections = { @@ -407,6 +409,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) -- ba=b markbase = nofregisteredmarks, markbasenode = base, markmark = mkmk, + checkmark = checkmark, } end else @@ -418,6 +421,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) -- ba=b markbase = nofregisteredmarks, markbasenode = base, markmark = mkmk, + checkmark = checkmark, }, } end @@ -1062,11 +1066,22 @@ local function inject_everything(head,where) ox = px - pn.markx -- report_injections("l2r case 3: %p",ox) -- end - local wn = getfield(n,"width") -- in arial marks have widths - if wn ~= 0 then - -- bad: we should center - pn.leftkern = -wn/2 - pn.rightkern = -wn/2 + if pn.checkmark then + local wn = getfield(n,"width") -- in arial marks have widths + if wn ~= 0 then + wn = wn/2 + if trace_injections then + report_injections("correcting non zero width mark %C",getchar(n)) + end + -- -- bad: we should center + -- pn.leftkern = -wn + -- pn.rightkern = -wn + -- -- we're too late anyway as kerns are already injected so + -- -- we do it the ugly way (no checking if the previous is + -- -- already a kern) .. maybe we should fix the font instead + insert_node_before(n,n,newkern(-wn)) + insert_node_after(n,n,newkern(-wn)) + end end end local oy = getfield(n,"yoffset") + getfield(p,"yoffset") + pn.marky @@ -1092,10 +1107,10 @@ local function inject_everything(head,where) nofmarks = nofmarks + 1 marks[nofmarks] = current else -local yoffset = i.yoffset -if yoffset and yoffset ~= 0 then - setfield(current,"yoffset",yoffset) -end + local yoffset = i.yoffset + if yoffset and yoffset ~= 0 then + setfield(current,"yoffset",yoffset) + end if hascursives then local cursivex = i.cursivex if cursivex then @@ -1148,10 +1163,6 @@ end end end -- left|glyph|right --- local yoffset = i.yoffset --- if yoffset and yoffset ~= 0 then --- setfield(current,"yoffset",yoffset) --- end local leftkern = i.leftkern if leftkern and leftkern ~= 0 then insert_node_before(head,current,newkern(leftkern)) @@ -1166,7 +1177,7 @@ end local i = p.emptyinjections if i then -- glyph|disc|glyph (special case) --- okay? + -- okay? local rightkern = i.rightkern if rightkern and rightkern ~= 0 then if next and getid(next) == disc_code then @@ -1561,6 +1572,7 @@ function injections.handler(head,where) if triggers then head = injectspaces(head) end + -- todo: marks only run too if nofregisteredmarks > 0 or nofregisteredcursives > 0 then if trace_injections then report_injections("injection variant %a","everything") diff --git a/src/fontloader/misc/fontloader-font-otl.lua b/src/fontloader/misc/fontloader-font-otl.lua index 94f6a45..0662290 100644 --- a/src/fontloader/misc/fontloader-font-otl.lua +++ b/src/fontloader/misc/fontloader-font-otl.lua @@ -52,7 +52,7 @@ local report_otf = logs.reporter("fonts","otf loading") local fonts = fonts local otf = fonts.handlers.otf -otf.version = 3.025 -- beware: also sync font-mis.lua and in mtx-fonts +otf.version = 3.026 -- beware: also sync font-mis.lua and in mtx-fonts otf.cache = containers.define("fonts", "otl", otf.version, true) otf.svgcache = containers.define("fonts", "svg", otf.version, true) otf.pdfcache = containers.define("fonts", "pdf", otf.version, true) diff --git a/src/fontloader/misc/fontloader-font-otr.lua b/src/fontloader/misc/fontloader-font-otr.lua index 7c81285..9cdbc3d 100644 --- a/src/fontloader/misc/fontloader-font-otr.lua +++ b/src/fontloader/misc/fontloader-font-otr.lua @@ -1747,118 +1747,6 @@ function readers.math(f,fontdata,specification) end end --- Goodie. A sequence instead of segments costs a bit more memory, some 300K on a --- dejavu serif and about the same on a pagella regular. - -local function packoutlines(data,makesequence) - local subfonts = data.subfonts - if subfonts then - for i=1,#subfonts do - packoutlines(subfonts[i],makesequence) - end - return - end - local common = data.segments - if common then - return - end - local glyphs = data.glyphs - if not glyphs then - return - end - if makesequence then - for index=1,#glyphs do - local glyph = glyphs[index] - local segments = glyph.segments - if segments then - local sequence = { } - local nofsequence = 0 - for i=1,#segments do - local segment = segments[i] - local nofsegment = #segment - nofsequence = nofsequence + 1 - sequence[nofsequence] = segment[nofsegment] - for i=1,nofsegment-1 do - nofsequence = nofsequence + 1 - sequence[nofsequence] = segment[i] - end - end - glyph.sequence = sequence - glyph.segments = nil - end - end - else - local hash = { } - local common = { } - local reverse = { } - local last = 0 - for index=1,#glyphs do - local segments = glyphs[index].segments - if segments then - for i=1,#segments do - local h = concat(segments[i]," ") - hash[h] = (hash[h] or 0) + 1 - end - end - end - for index=1,#glyphs do - local segments = glyphs[index].segments - if segments then - for i=1,#segments do - local segment = segments[i] - local h = concat(segment," ") - if hash[h] > 1 then -- minimal one shared in order to hash - local idx = reverse[h] - if not idx then - last = last + 1 - reverse[h] = last - common[last] = segment - idx = last - end - segments[i] = idx - end - end - end - end - if last > 0 then - data.segments = common - end - end -end - -local function unpackoutlines(data) - local subfonts = data.subfonts - if subfonts then - for i=1,#subfonts do - unpackoutlines(subfonts[i]) - end - return - end - local common = data.segments - if not common then - return - end - local glyphs = data.glyphs - if not glyphs then - return - end - for index=1,#glyphs do - local segments = glyphs[index].segments - if segments then - for i=1,#segments do - local c = common[segments[i]] - if c then - segments[i] = c - end - end - end - end - data.segments = nil -end - -otf.packoutlines = packoutlines -otf.unpackoutlines = unpackoutlines - -- Now comes the loader. The order of reading these matters as we need to know -- some properties in order to read following tables. When details is true we also -- initialize the glyphs data. @@ -2152,6 +2040,15 @@ function readers.loadshapes(filename,n) shapes = true, subfont = n, } + if fontdata then + -- easier on luajit but still we can hit the 64 K stack constants issue + for k, v in next, fontdata.glyphs do + v.class = nil + v.index = nil + v.math = nil + -- v.name = nil + end + end return fontdata and { -- version = 0.123 -- todo filename = filename, @@ -2302,63 +2199,3 @@ function readers.extend(fontdata) end end end - --- for now .. this will move to a context specific file - -if fonts.hashes then - - local identifiers = fonts.hashes.identifiers - local loadshapes = readers.loadshapes - - readers.version = 0.006 - readers.cache = containers.define("fonts", "shapes", readers.version, true) - - -- todo: loaders per format - - local function load(filename,sub) - local base = file.basename(filename) - local name = file.removesuffix(base) - local kind = file.suffix(filename) - local attr = lfs.attributes(filename) - local size = attr and attr.size or 0 - local time = attr and attr.modification or 0 - local sub = tonumber(sub) - if size > 0 and (kind == "otf" or kind == "ttf" or kind == "tcc") then - local hash = containers.cleanname(base) -- including suffix - if sub then - hash = hash .. "-" .. sub - end - data = containers.read(readers.cache,hash) - if not data or data.time ~= time or data.size ~= size then - data = loadshapes(filename,sub) - if data then - data.size = size - data.format = data.format or (kind == "otf" and "opentype") or "truetype" - data.time = time - packoutlines(data) - containers.write(readers.cache,hash,data) - data = containers.read(readers.cache,hash) -- frees old mem - end - end - unpackoutlines(data) - else - data = { - filename = filename, - size = 0, - time = time, - format = "unknown", - units = 1000, - glyphs = { } - } - end - return data - end - - fonts.hashes.shapes = table.setmetatableindex(function(t,k) - local d = identifiers[k] - local v = load(d.properties.filename,d.subindex) - t[k] = v - return v - end) - -end diff --git a/src/fontloader/misc/fontloader-font-ots.lua b/src/fontloader/misc/fontloader-font-ots.lua index d371156..da5d50a 100644 --- a/src/fontloader/misc/fontloader-font-ots.lua +++ b/src/fontloader/misc/fontloader-font-ots.lua @@ -254,6 +254,7 @@ local marks = false local currentfont = false local factor = 0 local threshold = 0 +local checkmarks = false local sweepnode = nil local sweepprev = nil @@ -959,7 +960,7 @@ function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode) local ba = markanchors[1][basechar] if ba then local ma = markanchors[2] - local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar]) + local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", pref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -1015,7 +1016,7 @@ function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlm local index = getligaindex(start) ba = ba[index] if ba then - local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar]) -- index + local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks) if trace_marks then logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", pref(dataset,sequence),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) @@ -1064,7 +1065,7 @@ function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode) local ba = markanchors[1][basechar] -- slot 1 has been made copy of the class hash if ba then local ma = markanchors[2] - local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true) + local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", pref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -1532,7 +1533,7 @@ function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlooku if ba then local ma = markanchors[2] if ma then - local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar]) + local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", cref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -1599,7 +1600,7 @@ function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentl local index = getligaindex(start) ba = ba[index] if ba then - local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar]) + local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", cref(dataset,sequence),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) @@ -1652,7 +1653,7 @@ function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlooku if ba then local ma = markanchors[2] if ma then - local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true) + local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", cref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -3381,6 +3382,7 @@ local function featuresprocessor(head,font,attr) marks = tfmdata.resources.marks threshold, factor = getthreshold(font) + checkmarks = tfmdata.properties.checkmarks elseif currentfont ~= font then @@ -3665,7 +3667,7 @@ if fontfeatures then function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr) local features = fontfeatures[font] - local enabled = features.spacekern == true and features.kern == true + local enabled = features and features.spacekern and features.kern if enabled then setspacekerns(font,sequence) end @@ -3677,7 +3679,7 @@ else -- generic (no hashes) function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr) local shared = fontdata[font].shared local features = shared and shared.features - local enabled = features and features.spacekern == true and features.kern == true + local enabled = features and features.spacekern and features.kern if enabled then setspacekerns(font,sequence) end @@ -3739,7 +3741,7 @@ local function spaceinitializer(tfmdata,value) -- attr if kern then if feat then for script, languages in next, kern do - local f = feat[k] + local f = feat[script] if f then for l in next, languages do f[l] = true @@ -3752,26 +3754,42 @@ local function spaceinitializer(tfmdata,value) -- attr feat = kern end for i=1,#steps do - local step = steps[i] + local step = steps[i] local coverage = step.coverage - if coverage then - local kerns = coverage[32] + local rules = step.rules + local format = step.format + if rules then + -- not now: analyze (simple) rules + elseif coverage then + -- what to do if we have no [1] but only [2] + local single = format == gpos_single + local kerns = coverage[32] if kerns then for k, v in next, kerns do - if type(v) == "table" then - right[k] = v[3] -- needs checking - else + if type(v) ~= "table" then right[k] = v + elseif single then + right[k] = v[3] + else + local one = v[1] + if one then + right[k] = one[3] + end end end end for k, v in next, coverage do local kern = v[32] if kern then - if type(kern) == "table" then - left[k] = kern[3] -- needs checking - else + if type(kern) ~= "table" then left[k] = kern + elseif single then + left[k] = v[3] + else + local one = v[1] + if one then + left[k] = one[3] + end end end end @@ -3822,3 +3840,17 @@ registerotffeature { node = spaceinitializer, }, } + +local function markinitializer(tfmdata,value) + local properties = tfmdata.properties + properties.checkmarks = value +end + +registerotffeature { + name = "checkmarks", + description = "check mark widths", + default = true, + initializers = { + node = markinitializer, + }, +} diff --git a/src/fontloader/misc/fontloader-l-table.lua b/src/fontloader/misc/fontloader-l-table.lua index d1e0592..498f518 100644 --- a/src/fontloader/misc/fontloader-l-table.lua +++ b/src/fontloader/misc/fontloader-l-table.lua @@ -478,7 +478,7 @@ function table.fromhash(t) return hsh end -local noquotes, hexify, handle, compact, inline, functions +local noquotes, hexify, handle, compact, inline, functions, metacheck local reserved = table.tohash { -- intercept a language inconvenience: no reserved words as key 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if', @@ -608,7 +608,8 @@ local function do_serialize(root,name,depth,level,indexed) if compact then last = #root for k=1,last do - if root[k] == nil then + -- if root[k] == nil then + if rawget(root,k) == nil then last = k - 1 break end @@ -817,6 +818,7 @@ local function serialize(_handle,root,name,specification) -- handle wins functions = specification.functions compact = specification.compact inline = specification.inline and compact + metacheck = specification.metacheck if functions == nil then functions = true end @@ -826,6 +828,9 @@ local function serialize(_handle,root,name,specification) -- handle wins if inline == nil then inline = compact end + if metacheck == nil then + metacheck = true + end else noquotes = false hexify = false @@ -833,6 +838,7 @@ local function serialize(_handle,root,name,specification) -- handle wins compact = true inline = true functions = true + metacheck = true end if tname == "string" then if name == "return" then @@ -857,8 +863,9 @@ local function serialize(_handle,root,name,specification) -- handle wins end if root then -- The dummy access will initialize a table that has a delayed initialization - -- using a metatable. (maybe explicitly test for metatable) - if getmetatable(root) then -- todo: make this an option, maybe even per subtable + -- using a metatable. (maybe explicitly test for metatable). This can crash on + -- metatables that check the index against a number. + if metacheck and getmetatable(root) then local dummy = root._w_h_a_t_e_v_e_r_ root._w_h_a_t_e_v_e_r_ = nil end diff --git a/src/fontloader/misc/fontloader-util-str.lua b/src/fontloader/misc/fontloader-util-str.lua index a54a4aa..42dbf16 100644 --- a/src/fontloader/misc/fontloader-util-str.lua +++ b/src/fontloader/misc/fontloader-util-str.lua @@ -10,7 +10,7 @@ utilities = utilities or { } utilities.strings = utilities.strings or { } local strings = utilities.strings -local format, gsub, rep, sub = string.format, string.gsub, string.rep, string.sub +local format, gsub, rep, sub, find = string.format, string.gsub, string.rep, string.sub, string.find local load, dump = load, string.dump local tonumber, type, tostring = tonumber, type, tostring local unpack, concat = table.unpack, table.concat @@ -385,6 +385,43 @@ function number.signed(i) end end +-- maybe to util-num + +local digit = patterns.digit +local period = patterns.period +local three = digit * digit * digit + +local splitter = Cs ( + (((1 - (three^1 * period))^1 + C(three)) * (Carg(1) * three)^1 + C((1-period)^1)) + * (P(1)/"" * Carg(2)) * C(2) +) + +patterns.formattednumber = splitter + +function number.formatted(n,sep1,sep2) + local s = type(s) == "string" and n or format("%0.2f",n) + if sep1 == true then + return lpegmatch(splitter,s,1,".",",") + elseif sep1 == "." then + return lpegmatch(splitter,s,1,sep1,sep2 or ",") + elseif sep1 == "," then + return lpegmatch(splitter,s,1,sep1,sep2 or ".") + else + return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".") + end +end + +-- print(number.formatted(1)) +-- print(number.formatted(12)) +-- print(number.formatted(123)) +-- print(number.formatted(1234)) +-- print(number.formatted(12345)) +-- print(number.formatted(123456)) +-- print(number.formatted(1234567)) +-- print(number.formatted(12345678)) +-- print(number.formatted(12345678,true)) +-- print(number.formatted(1234.56,"!","?")) + local zero = P("0")^1 / "" local plus = P("+") / "" local minus = P("-") @@ -732,43 +769,6 @@ local format_W = function(f) -- handy when doing depth related indent return format("nspaces[%s]",tonumber(f) or 0) end --- maybe to util-num - -local digit = patterns.digit -local period = patterns.period -local three = digit * digit * digit - -local splitter = Cs ( - (((1 - (three^1 * period))^1 + C(three)) * (Carg(1) * three)^1 + C((1-period)^1)) - * (P(1)/"" * Carg(2)) * C(2) -) - -patterns.formattednumber = splitter - -function number.formatted(n,sep1,sep2) - local s = type(s) == "string" and n or format("%0.2f",n) - if sep1 == true then - return lpegmatch(splitter,s,1,".",",") - elseif sep1 == "." then - return lpegmatch(splitter,s,1,sep1,sep2 or ",") - elseif sep1 == "," then - return lpegmatch(splitter,s,1,sep1,sep2 or ".") - else - return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".") - end -end - --- print(number.formatted(1)) --- print(number.formatted(12)) --- print(number.formatted(123)) --- print(number.formatted(1234)) --- print(number.formatted(12345)) --- print(number.formatted(123456)) --- print(number.formatted(1234567)) --- print(number.formatted(12345678)) --- print(number.formatted(12345678,true)) --- print(number.formatted(1234.56,"!","?")) - local format_m = function(f) n = n + 1 if not f or f == "" then @@ -801,9 +801,16 @@ end local format_extension = function(extensions,f,name) local extension = extensions[name] or "tostring(%s)" local f = tonumber(f) or 1 + local w = find(extension,"%.%.%.") if f == 0 then + if w then + extension = gsub(extension,"%.%.%.","") + end return extension elseif f == 1 then + if w then + extension = gsub(extension,"%.%.%.","%%s") + end n = n + 1 local a = "a" .. n return format(extension,a,a) -- maybe more times? @@ -811,6 +818,11 @@ local format_extension = function(extensions,f,name) local a = "a" .. (n + f + 1) return format(extension,a,a) else + if w then + extension = gsub(extension,"%.%.%.",rep("%%s,",f-1).."%%s") + end + -- we could fill an array and then n = n + 1 unpack(t,n,n+f) but as we + -- cache we don't save much and there are hardly any extensions anyway local t = { } for i=1,f do n = n + 1 diff --git a/src/fontloader/runtime/fontloader-basics-gen.lua b/src/fontloader/runtime/fontloader-basics-gen.lua index 871e548..8ea93cc 100644 --- a/src/fontloader/runtime/fontloader-basics-gen.lua +++ b/src/fontloader/runtime/fontloader-basics-gen.lua @@ -258,45 +258,83 @@ function caches.is_writable(path,name) return fullname and file.is_writable(fullname) end -function caches.loaddata(paths,name) - for i=1,#paths do - local data = false - local luaname, lucname = makefullname(paths[i],name) - if lucname and not lfs.isfile(lucname) and type(caches.compile) == "function" then - -- in case we used luatex and luajittex mixed ... lub or luc file - texio.write(string.format("(compiling luc: %s)",lucname)) - data = loadfile(luaname) - if data then - data = data() - end - if data then - caches.compile(data,luaname,lucname) - return data - end +-- function caches.loaddata(paths,name) +-- for i=1,#paths do +-- local data = false +-- local luaname, lucname = makefullname(paths[i],name) +-- if lucname and not lfs.isfile(lucname) and type(caches.compile) == "function" then +-- -- in case we used luatex and luajittex mixed ... lub or luc file +-- texio.write(string.format("(compiling luc: %s)",lucname)) +-- data = loadfile(luaname) +-- if data then +-- data = data() +-- end +-- if data then +-- caches.compile(data,luaname,lucname) +-- return data +-- end +-- end +-- if lucname and lfs.isfile(lucname) then -- maybe also check for size +-- texio.write(string.format("(load luc: %s)",lucname)) +-- data = loadfile(lucname) +-- if data then +-- data = data() +-- end +-- if data then +-- return data +-- else +-- texio.write(string.format("(loading failed: %s)",lucname)) +-- end +-- end +-- if luaname and lfs.isfile(luaname) then +-- texio.write(string.format("(load lua: %s)",luaname)) +-- data = loadfile(luaname) +-- if data then +-- data = data() +-- end +-- if data then +-- return data +-- end +-- end +-- end +-- end + +function caches.loaddata(readables,name,writable) + for i=1,#readables do + local path = readables[i] + local loader = false + local luaname, lucname = makefullname(path,name) + if lfs.isfile(lucname) then + loader = loadfile(lucname) end - if lucname and lfs.isfile(lucname) then -- maybe also check for size - texio.write(string.format("(load luc: %s)",lucname)) - data = loadfile(lucname) - if data then - data = data() + if not loader and lfs.isfile(luaname) then + -- can be different paths when we read a file database from disk + local luacrap, lucname = makefullname(writable,name) + texio.write(string.format("(compiling luc: %s)",lucname)) + if lfs.isfile(lucname) then + loader = loadfile(lucname) end - if data then - return data + caches.compile(data,luaname,lucname) + if lfs.isfile(lucname) then + texio.write(string.format("(load luc: %s)",lucname)) + loader = loadfile(lucname) else texio.write(string.format("(loading failed: %s)",lucname)) end - end - if luaname and lfs.isfile(luaname) then - texio.write(string.format("(load lua: %s)",luaname)) - data = loadfile(luaname) - if data then - data = data() - end - if data then - return data + if not loader then + texio.write(string.format("(load lua: %s)",luaname)) + loader = loadfile(luaname) + else + texio.write(string.format("(loading failed: %s)",luaname)) end end + if loader then + loader = loader() + collectgarbage("step") + return loader + end end + return false end function caches.savedata(path,name,data) diff --git a/src/fontloader/runtime/fontloader-reference.lua b/src/fontloader/runtime/fontloader-reference.lua index 6ef9430..6b899a1 100644 --- a/src/fontloader/runtime/fontloader-reference.lua +++ b/src/fontloader/runtime/fontloader-reference.lua @@ -1,6 +1,6 @@ -- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua -- parent file : c:/data/develop/context/sources/luatex-fonts.lua --- merge date : 08/11/16 13:56:03 +-- merge date : 08/27/16 13:35:36 do -- begin closure to overcome local limits and interference @@ -1352,7 +1352,7 @@ function table.fromhash(t) end return hsh end -local noquotes,hexify,handle,compact,inline,functions +local noquotes,hexify,handle,compact,inline,functions,metacheck local reserved=table.tohash { 'and','break','do','else','elseif','end','false','for','function','if', 'in','local','nil','not','or','repeat','return','then','true','until','while', @@ -1422,7 +1422,7 @@ local function do_serialize(root,name,depth,level,indexed) if compact then last=#root for k=1,last do - if root[k]==nil then + if rawget(root,k)==nil then last=k-1 break end @@ -1619,6 +1619,7 @@ local function serialize(_handle,root,name,specification) functions=specification.functions compact=specification.compact inline=specification.inline and compact + metacheck=specification.metacheck if functions==nil then functions=true end @@ -1628,6 +1629,9 @@ local function serialize(_handle,root,name,specification) if inline==nil then inline=compact end + if metacheck==nil then + metacheck=true + end else noquotes=false hexify=false @@ -1635,6 +1639,7 @@ local function serialize(_handle,root,name,specification) compact=true inline=true functions=true + metacheck=true end if tname=="string" then if name=="return" then @@ -1658,7 +1663,7 @@ local function serialize(_handle,root,name,specification) handle("t={") end if root then - if getmetatable(root) then + if metacheck and getmetatable(root) then local dummy=root._w_h_a_t_e_v_e_r_ root._w_h_a_t_e_v_e_r_=nil end @@ -3416,7 +3421,7 @@ if not modules then modules={} end modules ['util-str']={ utilities=utilities or {} utilities.strings=utilities.strings or {} local strings=utilities.strings -local format,gsub,rep,sub=string.format,string.gsub,string.rep,string.sub +local format,gsub,rep,sub,find=string.format,string.gsub,string.rep,string.sub,string.find local load,dump=load,string.dump local tonumber,type,tostring=tonumber,type,tostring local unpack,concat=table.unpack,table.concat @@ -3599,6 +3604,25 @@ function number.signed(i) return "-",-i end end +local digit=patterns.digit +local period=patterns.period +local three=digit*digit*digit +local splitter=Cs ( + (((1-(three^1*period))^1+C(three))*(Carg(1)*three)^1+C((1-period)^1))*(P(1)/""*Carg(2))*C(2) +) +patterns.formattednumber=splitter +function number.formatted(n,sep1,sep2) + local s=type(s)=="string" and n or format("%0.2f",n) + if sep1==true then + return lpegmatch(splitter,s,1,".",",") + elseif sep1=="." then + return lpegmatch(splitter,s,1,sep1,sep2 or ",") + elseif sep1=="," then + return lpegmatch(splitter,s,1,sep1,sep2 or ".") + else + return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".") + end +end local zero=P("0")^1/"" local plus=P("+")/"" local minus=P("-") @@ -3882,25 +3906,6 @@ end local format_W=function(f) return format("nspaces[%s]",tonumber(f) or 0) end -local digit=patterns.digit -local period=patterns.period -local three=digit*digit*digit -local splitter=Cs ( - (((1-(three^1*period))^1+C(three))*(Carg(1)*three)^1+C((1-period)^1))*(P(1)/""*Carg(2))*C(2) -) -patterns.formattednumber=splitter -function number.formatted(n,sep1,sep2) - local s=type(s)=="string" and n or format("%0.2f",n) - if sep1==true then - return lpegmatch(splitter,s,1,".",",") - elseif sep1=="." then - return lpegmatch(splitter,s,1,sep1,sep2 or ",") - elseif sep1=="," then - return lpegmatch(splitter,s,1,sep1,sep2 or ".") - else - return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".") - end -end local format_m=function(f) n=n+1 if not f or f=="" then @@ -3925,9 +3930,16 @@ end local format_extension=function(extensions,f,name) local extension=extensions[name] or "tostring(%s)" local f=tonumber(f) or 1 + local w=find(extension,"%.%.%.") if f==0 then + if w then + extension=gsub(extension,"%.%.%.","") + end return extension elseif f==1 then + if w then + extension=gsub(extension,"%.%.%.","%%s") + end n=n+1 local a="a"..n return format(extension,a,a) @@ -3935,6 +3947,9 @@ local format_extension=function(extensions,f,name) local a="a"..(n+f+1) return format(extension,a,a) else + if w then + extension=gsub(extension,"%.%.%.",rep("%%s,",f-1).."%%s") + end local t={} for i=1,f do n=n+1 @@ -4466,44 +4481,41 @@ function caches.is_writable(path,name) local fullname=makefullname(path,name) return fullname and file.is_writable(fullname) end -function caches.loaddata(paths,name) - for i=1,#paths do - local data=false - local luaname,lucname=makefullname(paths[i],name) - if lucname and not lfs.isfile(lucname) and type(caches.compile)=="function" then +function caches.loaddata(readables,name,writable) + for i=1,#readables do + local path=readables[i] + local loader=false + local luaname,lucname=makefullname(path,name) + if lfs.isfile(lucname) then + loader=loadfile(lucname) + end + if not loader and lfs.isfile(luaname) then + local luacrap,lucname=makefullname(writable,name) texio.write(string.format("(compiling luc: %s)",lucname)) - data=loadfile(luaname) - if data then - data=data() - end - if data then - caches.compile(data,luaname,lucname) - return data - end - end - if lucname and lfs.isfile(lucname) then - texio.write(string.format("(load luc: %s)",lucname)) - data=loadfile(lucname) - if data then - data=data() + if lfs.isfile(lucname) then + loader=loadfile(lucname) end - if data then - return data + caches.compile(data,luaname,lucname) + if lfs.isfile(lucname) then + texio.write(string.format("(load luc: %s)",lucname)) + loader=loadfile(lucname) else texio.write(string.format("(loading failed: %s)",lucname)) end - end - if luaname and lfs.isfile(luaname) then - texio.write(string.format("(load lua: %s)",luaname)) - data=loadfile(luaname) - if data then - data=data() - end - if data then - return data + if not loader then + texio.write(string.format("(load lua: %s)",luaname)) + loader=loadfile(luaname) + else + texio.write(string.format("(loading failed: %s)",luaname)) end end + if loader then + loader=loader() + collectgarbage("step") + return loader + end end + return false end function caches.savedata(path,name,data) local luaname,lucname=makefullname(path,name) @@ -4630,7 +4642,7 @@ function containers.read(container,name) local storage=container.storage local stored=storage[name] if not stored and container.enabled and caches and containers.usecache then - stored=caches.loaddata(container.readables,name) + stored=caches.loaddata(container.readables,name,container.writable) if stored and stored.cache_version==container.version then if trace_cache or trace_containers then report_containers("action %a, category %a, name %a","load",container.subcategory,name) @@ -8845,112 +8857,6 @@ function readers.math(f,fontdata,specification) reportskippedtable("math") end end -local function packoutlines(data,makesequence) - local subfonts=data.subfonts - if subfonts then - for i=1,#subfonts do - packoutlines(subfonts[i],makesequence) - end - return - end - local common=data.segments - if common then - return - end - local glyphs=data.glyphs - if not glyphs then - return - end - if makesequence then - for index=1,#glyphs do - local glyph=glyphs[index] - local segments=glyph.segments - if segments then - local sequence={} - local nofsequence=0 - for i=1,#segments do - local segment=segments[i] - local nofsegment=#segment - nofsequence=nofsequence+1 - sequence[nofsequence]=segment[nofsegment] - for i=1,nofsegment-1 do - nofsequence=nofsequence+1 - sequence[nofsequence]=segment[i] - end - end - glyph.sequence=sequence - glyph.segments=nil - end - end - else - local hash={} - local common={} - local reverse={} - local last=0 - for index=1,#glyphs do - local segments=glyphs[index].segments - if segments then - for i=1,#segments do - local h=concat(segments[i]," ") - hash[h]=(hash[h] or 0)+1 - end - end - end - for index=1,#glyphs do - local segments=glyphs[index].segments - if segments then - for i=1,#segments do - local segment=segments[i] - local h=concat(segment," ") - if hash[h]>1 then - local idx=reverse[h] - if not idx then - last=last+1 - reverse[h]=last - common[last]=segment - idx=last - end - segments[i]=idx - end - end - end - end - if last>0 then - data.segments=common - end - end -end -local function unpackoutlines(data) - local subfonts=data.subfonts - if subfonts then - for i=1,#subfonts do - unpackoutlines(subfonts[i]) - end - return - end - local common=data.segments - if not common then - return - end - local glyphs=data.glyphs - if not glyphs then - return - end - for index=1,#glyphs do - local segments=glyphs[index].segments - if segments then - for i=1,#segments do - local c=common[segments[i]] - if c then - segments[i]=c - end - end - end - end - data.segments=nil -end -otf.packoutlines=packoutlines -otf.unpackoutlines=unpackoutlines local function getinfo(maindata,sub,platformnames,rawfamilynames) local fontdata=sub and maindata.subfonts and maindata.subfonts[sub] or maindata local names=fontdata.names @@ -9218,6 +9124,13 @@ function readers.loadshapes(filename,n) shapes=true, subfont=n, } + if fontdata then + for k,v in next,fontdata.glyphs do + v.class=nil + v.index=nil + v.math=nil + end + end return fontdata and { filename=filename, format=fontdata.format, @@ -9347,56 +9260,6 @@ function readers.extend(fontdata) end end end -if fonts.hashes then - local identifiers=fonts.hashes.identifiers - local loadshapes=readers.loadshapes - readers.version=0.006 - readers.cache=containers.define("fonts","shapes",readers.version,true) - local function load(filename,sub) - local base=file.basename(filename) - local name=file.removesuffix(base) - local kind=file.suffix(filename) - local attr=lfs.attributes(filename) - local size=attr and attr.size or 0 - local time=attr and attr.modification or 0 - local sub=tonumber(sub) - if size>0 and (kind=="otf" or kind=="ttf" or kind=="tcc") then - local hash=containers.cleanname(base) - if sub then - hash=hash.."-"..sub - end - data=containers.read(readers.cache,hash) - if not data or data.time~=time or data.size~=size then - data=loadshapes(filename,sub) - if data then - data.size=size - data.format=data.format or (kind=="otf" and "opentype") or "truetype" - data.time=time - packoutlines(data) - containers.write(readers.cache,hash,data) - data=containers.read(readers.cache,hash) - end - end - unpackoutlines(data) - else - data={ - filename=filename, - size=0, - time=time, - format="unknown", - units=1000, - glyphs={} - } - end - return data - end - fonts.hashes.shapes=table.setmetatableindex(function(t,k) - local d=identifiers[k] - local v=load(d.properties.filename,d.subindex) - t[k]=v - return v - end) -end end -- closure @@ -9415,6 +9278,7 @@ local concat,remove=table.concat,table.remove local floor,abs,round,ceil=math.floor,math.abs,math.round,math.ceil local P,C,R,S,C,Cs,Ct=lpeg.P,lpeg.C,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Ct local lpegmatch=lpeg.match +local formatters=string.formatters local readers=fonts.handlers.otf.readers local streamreader=readers.streamreader local readbytes=streamreader.readbytes @@ -9815,6 +9679,7 @@ do local ymax=0 local checked=false local keepcurve=false + local version=2 local function showstate(where) report("%w%-10s : [%s] n=%i",depth*2,where,concat(stack," ",1,top),top) end @@ -10237,9 +10102,91 @@ do return floor((stems+7)/8) end end - local function unsupported() + local function unsupported(t) if trace_charstrings then - showstate("unsupported") + showstate("unsupported "..t) + end + top=0 + end + local function unsupportedsub(t) + if trace_charstrings then + showstate("unsupported sub "..t) + end + top=0 + end + local function getstem3() + if trace_charstrings then + showstate("stem3") + end + top=0 + end + local function divide() + if version==1 then + local d=stack[top] + top=top-1 + stack[top]=stack[top]/d + end + end + local function closepath() + if version==1 then + if trace_charstrings then + showstate("closepath") + end + end + top=0 + end + local function hsbw() + if version==1 then + if trace_charstrings then + showstate("dotsection") + end + width=stack[top] + end + top=0 + end + local function seac() + if version==1 then + if trace_charstrings then + showstate("seac") + end + end + top=0 + end + local function sbw() + if version==1 then + if trace_charstrings then + showstate("sbw") + end + width=stack[top-1] + end + top=0 + end + local function callothersubr() + if version==1 then + if trace_charstrings then + showstate("callothersubr (unsupported)") + end + end + top=0 + end + local function pop() + if version==1 then + if trace_charstrings then + showstate("pop (unsupported)") + end + top=top+1 + stack[top]=0 + else + top=0 + end + end + local function setcurrentpoint() + if version==1 then + if trace_charstrings then + showstate("pop (unsupported)") + end + x=x+stack[top-1] + y=y+stack[top] end top=0 end @@ -10256,7 +10203,7 @@ do unsupported, unsupported, unsupported, - unsupported, + hsbw, unsupported, unsupported, unsupported, @@ -10277,6 +10224,15 @@ do hvcurveto, } local subactions={ + [000]=dotsection, + [001]=getstem3, + [002]=getstem3, + [006]=seac, + [007]=sbw, + [012]=divide, + [016]=callothersubr, + [017]=pop, + [033]=setcurrentpoint, [034]=hflex, [035]=flex, [036]=hflex1, @@ -10284,23 +10240,29 @@ do } local p_bytes=Ct((P(1)/byte)^0) local function call(scope,list,bias,process) - local index=stack[top]+bias - top=top-1 - if trace_charstrings then - showvalue(scope,index,true) - end - local str=list[index] - if str then - if type(str)=="string" then - str=lpegmatch(p_bytes,str) - list[index]=str - end - depth=depth+1 - process(str) - depth=depth-1 + depth=depth+1 + if top==0 then + showstate(formatters["unknown %s call"](scope)) + top=0 else - report("unknown %s %i",scope,index) + local index=stack[top]+bias + top=top-1 + if trace_charstrings then + showvalue(scope,index,true) + end + local tab=list[index] + if tab then + if type(tab)=="string" then + tab=lpegmatch(p_bytes,tab) + list[index]=tab + end + process(tab) + else + showstate(formatters["unknown %s call %i"](scope,index)) + top=0 + end end + depth=depth-1 end local function process(tab) local i=1 @@ -10367,7 +10329,7 @@ do local t=tab[i] local a=subactions[t] if a then - a() + a(t) else if trace_charstrings then showvalue("",t) @@ -10378,7 +10340,7 @@ do else local a=actions[t] if a then - local s=a() + local s=a(t) if s then i=i+s end @@ -10392,25 +10354,38 @@ do end end end - parsecharstrings=function(data,glyphs,doshapes) + local function setbias(globals,locals) + if version==1 then + return + false, + false + else + local g,l=#globals,#locals + return + ((g<1240 and 107) or (g<33900 and 1131) or 32768)+1, + ((l<1240 and 107) or (l<33900 and 1131) or 32768)+1 + end + end + parsecharstrings=function(data,glyphs,doshapes,tversion) local dictionary=data.dictionaries[1] local charstrings=dictionary.charstrings local charset=dictionary.charset + local private=dictionary.private or { data={} } keepcurve=doshapes + version=tversion stack={} glyphs=glyphs or {} strings=data.strings - locals=dictionary.subroutines - globals=data.routines - globalbias=#globals - localbias=#locals - globalbias=((globalbias<1240 and 107) or (globalbias<33900 and 1131) or 32768)+1 - localbias=((localbias<1240 and 107) or (localbias<33900 and 1131) or 32768)+1 - local nominalwidth=dictionary.private.data.nominalwidthx or 0 - local defaultwidth=dictionary.private.data.defaultwidthx or 0 + globals=data.routines or {} + locals=dictionary.subroutines or {} + globalbias,localbias=setbias(globals,locals) + local nominalwidth=private.data.nominalwidthx or 0 + local defaultwidth=private.data.defaultwidthx or 0 for i=1,#charstrings do - local str=charstrings[i] - local tab=lpegmatch(p_bytes,str) + local tab=charstrings[i] + if type(tab)=="string" then + tab=lpegmatch(p_bytes,tab) + end local index=i-1 x=0 y=0 @@ -10461,19 +10436,19 @@ do end return glyphs end - parsecharstring=function(data,dictionary,charstring,glyphs,index,doshapes) + parsecharstring=function(data,dictionary,tab,glyphs,index,doshapes,tversion) local private=dictionary.private keepcurve=doshapes + version=tversion strings=data.strings locals=dictionary.subroutines or {} globals=data.routines or {} - globalbias=#globals - localbias=#locals - globalbias=((globalbias<1240 and 107) or (globalbias<33900 and 1131) or 32768)+1 - localbias=((localbias<1240 and 107) or (localbias<33900 and 1131) or 32768)+1 + globalbias,localbias=setbias(globals,locals) local nominalwidth=private and private.data.nominalwidthx or 0 local defaultwidth=private and private.data.defaultwidthx or 0 - local tab=lpegmatch(p_bytes,charstring) + if type(tab)=="string" then + tab=lpegmatch(p_bytes,tab) + end x=0 y=0 width=false @@ -10497,7 +10472,7 @@ do else width=nominalwidth+width end -index=index-1 + index=index-1 local glyph=glyphs[index] if not glyph then glyphs[index]={ @@ -10520,7 +10495,6 @@ index=index-1 report("width: %s",tostring(width)) report("boundingbox: % t",boundingbox) end - return charstring end resetcharstrings=function() result={} @@ -10634,7 +10608,7 @@ local function readcidprivates(f,data) end parseprivates(data,dictionaries) end -local function readnoselect(f,data,glyphs,doshapes) +local function readnoselect(f,data,glyphs,doshapes,version) local dictionaries=data.dictionaries local dictionary=dictionaries[1] readglobals(f,data) @@ -10644,10 +10618,11 @@ local function readnoselect(f,data,glyphs,doshapes) readprivates(f,data) parseprivates(data,data.dictionaries) readlocals(f,data,dictionary) - parsecharstrings(data,glyphs,doshapes) + parsecharstrings(data,glyphs,doshapes,version) resetcharstrings() end -local function readfdselect(f,data,glyphs,doshapes) +readers.parsecharstrings=parsecharstrings +local function readfdselect(f,data,glyphs,doshapes,version) local header=data.header local dictionaries=data.dictionaries local dictionary=dictionaries[1] @@ -10705,7 +10680,7 @@ local function readfdselect(f,data,glyphs,doshapes) readlocals(f,data,dictionaries[i]) end for i=1,#charstrings do - parsecharstring(data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes) + parsecharstring(data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version) end resetcharstrings() end @@ -11199,6 +11174,7 @@ local bittest=bit32.btest local rshift=bit32.rshift local concat=table.concat local lower=string.lower +local copy=table.copy local sub=string.sub local strip=string.strip local tohash=table.tohash @@ -12632,7 +12608,7 @@ do markclass=d.markclass or nil, flags=d.flags, } - sublookuplist[nofsublookups]=h + sublookuplist[nofsublookups]=copy(h) sublookuphash[lookupid]=nofsublookups sublookupcheck[lookupid]=1 else @@ -15344,7 +15320,7 @@ local trace_defining=false registertracker("fonts.defining",function(v) trace_de local report_otf=logs.reporter("fonts","otf loading") local fonts=fonts local otf=fonts.handlers.otf -otf.version=3.025 +otf.version=3.026 otf.cache=containers.define("fonts","otl",otf.version,true) otf.svgcache=containers.define("fonts","svg",otf.version,true) otf.pdfcache=containers.define("fonts","pdf",otf.version,true) @@ -16624,7 +16600,7 @@ function injections.setkern(current,factor,rlmode,x,injection) return 0,0 end end -function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) +function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmark) local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2]) nofregisteredmarks=nofregisteredmarks+1 if rlmode>=0 then @@ -16642,6 +16618,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) i.markbase=nofregisteredmarks i.markbasenode=base i.markmark=mkmk + i.checkmark=checkmark end else p.injections={ @@ -16651,6 +16628,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) markbase=nofregisteredmarks, markbasenode=base, markmark=mkmk, + checkmark=checkmark, } end else @@ -16662,6 +16640,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) markbase=nofregisteredmarks, markbasenode=base, markmark=mkmk, + checkmark=checkmark, }, } end @@ -17213,10 +17192,16 @@ local function inject_everything(head,where) end else ox=px-pn.markx - local wn=getfield(n,"width") - if wn~=0 then - pn.leftkern=-wn/2 - pn.rightkern=-wn/2 + if pn.checkmark then + local wn=getfield(n,"width") + if wn~=0 then + wn=wn/2 + if trace_injections then + report_injections("correcting non zero width mark %C",getchar(n)) + end + insert_node_before(n,n,newkern(-wn)) + insert_node_after(n,n,newkern(-wn)) + end end end local oy=getfield(n,"yoffset")+getfield(p,"yoffset")+pn.marky @@ -17240,10 +17225,10 @@ local function inject_everything(head,where) nofmarks=nofmarks+1 marks[nofmarks]=current else -local yoffset=i.yoffset -if yoffset and yoffset~=0 then - setfield(current,"yoffset",yoffset) -end + local yoffset=i.yoffset + if yoffset and yoffset~=0 then + setfield(current,"yoffset",yoffset) + end if hascursives then local cursivex=i.cursivex if cursivex then @@ -17884,33 +17869,40 @@ local mappers={ } local classifiers=characters.classifiers if not classifiers then - local first_arabic,last_arabic=characters.blockrange("arabic") - local first_syriac,last_syriac=characters.blockrange("syriac") - local first_mandiac,last_mandiac=characters.blockrange("mandiac") - local first_nko,last_nko=characters.blockrange("nko") + local f_arabic,l_arabic=characters.blockrange("arabic") + local f_syriac,l_syriac=characters.blockrange("syriac") + local f_mandiac,l_mandiac=characters.blockrange("mandiac") + local f_nko,l_nko=characters.blockrange("nko") + local f_ext_a,l_ext_a=characters.blockrange("arabicextendeda") classifiers=table.setmetatableindex(function(t,k) - local c=chardata[k] - local v=false - if c then - local arabic=c.arabic - if arabic then - v=mappers[arabic] - if not v then - log.report("analyze","error in mapping arabic %C",k) - v=false - end - elseif k>=first_arabic and k<=last_arabic or k>=first_syriac and k<=last_syriac or - k>=first_mandiac and k<=last_mandiac or k>=first_nko and k<=last_nko then - if categories[k]=="mn" then - v=s_mark - else - v=s_rest + if type(k)=="number" then + local c=chardata[k] + local v=false + if c then + local arabic=c.arabic + if arabic then + v=mappers[arabic] + if not v then + log.report("analyze","error in mapping arabic %C",k) + v=false + end + elseif (k>=f_arabic and k<=l_arabic) or + (k>=f_syriac and k<=l_syriac) or + (k>=f_mandiac and k<=l_mandiac) or + (k>=f_nko and k<=l_nko) or + (k>=f_ext_a and k<=l_ext_a) then + if categories[k]=="mn" then + v=s_mark + else + v=s_rest + end end end + t[k]=v + return v end - t[k]=v - return v end) + characters.classifiers=classifiers end function methods.arab(head,font,attr) local first,last=nil,nil @@ -18180,6 +18172,7 @@ local marks=false local currentfont=false local factor=0 local threshold=0 +local checkmarks=false local sweepnode=nil local sweepprev=nil local sweepnext=nil @@ -18795,7 +18788,7 @@ function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode) local ba=markanchors[1][basechar] if ba then local ma=markanchors[2] - local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar]) + local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", pref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -18850,7 +18843,7 @@ function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlm local index=getligaindex(start) ba=ba[index] if ba then - local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar]) + local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks) if trace_marks then logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", pref(dataset,sequence),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) @@ -18897,7 +18890,7 @@ function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode) local ba=markanchors[1][basechar] if ba then local ma=markanchors[2] - local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true) + local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", pref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -19279,7 +19272,7 @@ function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlooku if ba then local ma=markanchors[2] if ma then - local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar]) + local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", cref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -19345,7 +19338,7 @@ function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentl local index=getligaindex(start) ba=ba[index] if ba then - local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar]) + local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", cref(dataset,sequence),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) @@ -19397,7 +19390,7 @@ function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlooku if ba then local ma=markanchors[2] if ma then - local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true) + local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", cref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -20837,6 +20830,7 @@ local function featuresprocessor(head,font,attr) marks=tfmdata.resources.marks threshold, factor=getthreshold(font) + checkmarks=tfmdata.properties.checkmarks elseif currentfont~=font then report_warning("nested call with a different font, level %s, quitting",nesting) nesting=nesting-1 @@ -21057,7 +21051,7 @@ local setspacekerns=nodes.injections.setspacekerns if not setspacekerns then os. if fontfeatures then function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr) local features=fontfeatures[font] - local enabled=features.spacekern==true and features.kern==true + local enabled=features and features.spacekern and features.kern if enabled then setspacekerns(font,sequence) end @@ -21067,7 +21061,7 @@ else function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr) local shared=fontdata[font].shared local features=shared and shared.features - local enabled=features and features.spacekern==true and features.kern==true + local enabled=features and features.spacekern and features.kern if enabled then setspacekerns(font,sequence) end @@ -21122,7 +21116,7 @@ local function spaceinitializer(tfmdata,value) if kern then if feat then for script,languages in next,kern do - local f=feat[k] + local f=feat[script] if f then for l in next,languages do f[l]=true @@ -21137,24 +21131,38 @@ local function spaceinitializer(tfmdata,value) for i=1,#steps do local step=steps[i] local coverage=step.coverage - if coverage then + local rules=step.rules + local format=step.format + if rules then + elseif coverage then + local single=format==gpos_single local kerns=coverage[32] if kerns then for k,v in next,kerns do - if type(v)=="table" then - right[k]=v[3] - else + if type(v)~="table" then right[k]=v + elseif single then + right[k]=v[3] + else + local one=v[1] + if one then + right[k]=one[3] + end end end end for k,v in next,coverage do local kern=v[32] if kern then - if type(kern)=="table" then - left[k]=kern[3] - else + if type(kern)~="table" then left[k]=kern + elseif single then + left[k]=v[3] + else + local one=v[1] + if one then + left[k]=one[3] + end end end end @@ -21202,6 +21210,18 @@ registerotffeature { node=spaceinitializer, }, } +local function markinitializer(tfmdata,value) + local properties=tfmdata.properties + properties.checkmarks=value +end +registerotffeature { + name="checkmarks", + description="check mark widths", + default=true, + initializers={ + node=markinitializer, + }, +} end -- closure @@ -21605,9 +21625,11 @@ local function initializedevanagi(tfmdata) local steps=sequence.steps local nofsteps=sequence.nofsteps local features=sequence.features - if features["rphf"] then + local has_rphf=features.rphf + local has_blwf=features.blwf + if has_rphf and has_rphf.deva then devanagari.reph=true - elseif features["blwf"] then + elseif has_blwf and has_blwf.deva then devanagari.vattu=true for i=1,nofsteps do local step=steps[i] @@ -21621,57 +21643,60 @@ local function initializedevanagi(tfmdata) end end end - if valid[kind] then - for i=1,nofsteps do - local step=steps[i] - local coverage=step.coverage - if coverage then - local reph=false - if step.osdstep then - for k,v in next,ra do - local r=coverage[k] - if r then - local h=false - for k,v in next,halant do - local h=r[k] - if h then - reph=h.ligature or false - break + for kind,spec in next,features do + if spec.dev2 and valid[kind] then + for i=1,nofsteps do + local step=steps[i] + local coverage=step.coverage + if coverage then + local reph=false + if kind=="rphf" then + if true then + for k,v in next,ra do + local r=coverage[k] + if r then + local h=false + for k,v in next,halant do + local h=r[k] + if h then + reph=h.ligature or false + break + end + end + if reph then + break + end end end - if reph then - break - end + else end end - else + seqsubset[#seqsubset+1]={ kind,coverage,reph } end - seqsubset[#seqsubset+1]={ kind,coverage,reph } end end - end - if kind=="pref" then - local sequence=dataset[3] - local steps=sequence.steps - local nofsteps=sequence.nofsteps - for i=1,nofsteps do - local step=steps[i] - local coverage=step.coverage - if coverage then - for k,v in next,halant do - local h=coverage[k] - if h then - local found=false - for k,v in next,h do - found=v and v.ligature + if kind=="pref" then + local steps=sequence.steps + local nofsteps=sequence.nofsteps + for i=1,nofsteps do + local step=steps[i] + local coverage=step.coverage + if coverage then + for k,v in next,halant do + local h=coverage[k] + if h then + local found=false + for k,v in next,h do + found=v and v.ligature + if found then + pre_base_reordering_consonants[k]=found + break + end + end if found then - pre_base_reordering_consonants[k]=found break end end - if found then - break - end end end end @@ -22058,6 +22083,8 @@ function handlers.devanagari_reorder_matras(head,start) start=startnext break end + else + break end current=next end @@ -22070,12 +22097,12 @@ function handlers.devanagari_reorder_reph(head,start) local startfont=getfont(start) local startattr=getprop(start,a_syllabe) while current do - local char=ischar(current,font) + local char=ischar(current,startfont) if char and getprop(current,a_syllabe)==startattr then if halant[char] and not getprop(current,a_state) then local next=getnext(current) if next then - local nextchar=ischar(next,font) + local nextchar=ischar(next,startfont) if nextchar and zw_char[nextchar] and getprop(next,a_syllabe)==startattr then current=next next=getnext(current) @@ -22097,7 +22124,7 @@ function handlers.devanagari_reorder_reph(head,start) if not startnext then current=getnext(start) while current do - local char=ischar(current,font) + local char=ischar(current,startfont) if char and getprop(current,a_syllabe)==startattr then if getprop(current,a_state)==s_pstf then startnext=getnext(start) @@ -22119,7 +22146,7 @@ function handlers.devanagari_reorder_reph(head,start) current=getnext(start) local c=nil while current do - local char=ischar(current,font) + local char=ischar(current,startfont) if char and getprop(current,a_syllabe)==startattr then if not c and mark_above_below_post[char] and reorder_class[char]~="after subscript" then c=current @@ -22143,7 +22170,7 @@ function handlers.devanagari_reorder_reph(head,start) current=start local next=getnext(current) while next do - local nextchar=ischar(next,font) + local nextchar=ischar(next,startfont) if nextchar and getprop(next,a_syllabe)==startattr then current=next next=getnext(current) @@ -22156,7 +22183,7 @@ function handlers.devanagari_reorder_reph(head,start) head=remove_node(head,start) local next=getnext(current) setlink(start,next) - setlink(current,"next",start) + setlink(current,start) start=startnext end end @@ -22169,12 +22196,12 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) local startfont=getfont(start) local startattr=getprop(start,a_syllabe) while current do - local char=ischar(current,font) + local char=ischar(current,startfont) if char and getprop(current,a_syllabe)==startattr then local next=getnext(current) if halant[char] and not getprop(current,a_state) then if next then - local nextchar=ischar(next,font) + local nextchar=ischar(next,startfont) if nextchar and getprop(next,a_syllabe)==startattr then if nextchar==c_zwnj or nextchar==c_zwj then current=next @@ -22198,13 +22225,13 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) current=getnext(start) startattr=getprop(start,a_syllabe) while current do - local char=ischar(current,font) + local char=ischar(current,startfont) if char and getprop(current,a_syllabe)==startattr then if not consonant[char] and getprop(current,a_state) then startnext=getnext(start) removenode(start,start) local prev=getprev(current) - setlink(start,prev) + setlink(prev,start) setlink(start,current) start=startnext break @@ -22264,21 +22291,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) local kind=subset[1] local lookupcache=subset[2] if kind=="rphf" then - for k,v in next,ra do - local r=lookupcache[k] - if r then - for k,v in next,halant do - local h=r[k] - if h then - reph=h.ligature or false - break - end - end - if reph then - break - end - end - end + reph=subset[3] local current=start local last=getnext(stop) while current~=last do @@ -22311,7 +22324,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) if current~=stop then local c=locl[current] or getchar(current) local found=lookupcache[c] - if found then + if found then local next=getnext(current) local n=locl[next] or getchar(next) if found[n] then @@ -22538,7 +22551,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) local prev=getprev(current) if prev~=target then local next=getnext(current) - setlink(next,prev) + setlink(prev,next) if current==stop then stop=prev end @@ -22569,7 +22582,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) stop=current end local prev=getprev(c) - setlink(next,prev) + setlink(prev,next) local nextnext=getnext(next) setnext(current,nextnext) local nextnextnext=getnext(nextnext) @@ -22582,6 +22595,9 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) current=getnext(current) end if getchar(base)==c_nbsp then + if base==stop then + stop=getprev(stop) + end nbspaces=nbspaces-1 head=remove_node(head,base) flush_node(base) @@ -22623,7 +22639,7 @@ local function analyze_next_chars_one(c,font,variant) elseif (vv==c_zwnj or vv==c_zwj) and halant[vvv] then local nnnn=getnext(nnn) if nnnn then - local vvvv=ischar(nnnn) + local vvvv=ischar(nnnn,font) if vvvv and consonant[vvvv] then c=nnnn end @@ -22646,7 +22662,7 @@ local function analyze_next_chars_one(c,font,variant) local nn=getnext(n) if nn then local vv=ischar(nn,font) - if vv and zw_char[vv] then + if vv and zw_char[v] then n=nn v=vv nn=getnext(nn) @@ -24226,26 +24242,25 @@ handlers.afm=afm local readers=afm.readers or {} afm.readers=readers afm.version=1.512 -local get_indexes +local get_indexes,get_shapes do - local n,m - local progress=function(str,position,name,size) - local forward=position+tonumber(size)+3+2 - n=n+1 - if n>=m then - return #str,name - elseif forward<#str then - return forward,name - else - return #str,name + local decrypt + do + local r,c1,c2,n=0,0,0,0 + local function step(c) + local cipher=byte(c) + local plain=bxor(cipher,rshift(r,8)) + r=((cipher+r)*c1+c2)%65536 + return char(plain) + end + decrypt=function(binary,initial,seed) + r,c1,c2,n=initial,52845,22719,seed + binary=gsub(binary,".",step) + return sub(binary,n+1) end - end - local initialize=function(str,position,size) - n=0 - m=size - return position+1 end local charstrings=P("/CharStrings") + local subroutines=P("/Subrs") local encoding=P("/Encoding") local dup=P("dup") local put=P("put") @@ -24255,28 +24270,58 @@ do local cardinal=digits/tonumber local spaces=P(" ")^1 local spacing=patterns.whitespace^0 + local routines,vector,chars,n,m + local initialize=function(str,position,size) + n=0 + m=size + return position+1 + end + local setroutine=function(str,position,index,size) + local forward=position+tonumber(size) + local stream=sub(str,position+1,forward) + routines[index]=decrypt(stream,4330,4) + return forward + end + local setvector=function(str,position,name,size) + local forward=position+tonumber(size) + if n>=m then + return #str + elseif forward<#str then + vector[n]=name + n=n+1 + return forward + else + return #str + end + end + local setshapes=function(str,position,name,size) + local forward=position+tonumber(size) + local stream=sub(str,position+1,forward) + if n>m then + return #str + elseif forward<#str then + vector[n]=name + n=n+1 + chars [n]=decrypt(stream,4330,4) + return forward + else + return #str + end + end + local p_rd=spacing*(P("RD")+P("-|")) + local p_np=spacing*(P("NP")+P("|")) + local p_nd=spacing*(P("ND")+P("|")) + local p_filterroutines= + (1-subroutines)^0*subroutines*spaces*Cmt(cardinal,initialize)*(Cmt(cardinal*spaces*cardinal*p_rd,setroutine)*p_np+P(1))^1 + local p_filtershapes= + (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal*p_rd,setshapes)*p_nd+P(1))^1 local p_filternames=Ct ( - (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal,progress)+P(1))^1 + (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal,setvector)+P(1))^1 ) local p_filterencoding=(1-encoding)^0*encoding*spaces*digits*spaces*array*(1-dup)^0*Cf( Ct("")*Cg(spacing*dup*spaces*cardinal*spaces*name*spaces*put)^1 ,rawset) - local decrypt - do - local r,c1,c2,n=0,0,0,0 - local function step(c) - local cipher=byte(c) - local plain=bxor(cipher,rshift(r,8)) - r=((cipher+r)*c1+c2)%65536 - return char(plain) - end - decrypt=function(binary) - r,c1,c2,n=55665,52845,22719,4 - binary=gsub(binary,".",step) - return sub(binary,n+1) - end - end - local function loadpfbvector(filename) + local function loadpfbvector(filename,shapestoo) local data=io.loaddata(resolvers.findfile(filename)) if not data then report_pfb("no data in %a",filename) @@ -24291,18 +24336,30 @@ do report_pfb("no binary data in %a",filename) return end - binary=decrypt(binary,4) - local vector=lpegmatch(p_filternames,binary) - for i=1,#vector do - vector[i-1]=vector[i] - end - vector[#vector]=nil - if not vector then - report_pfb("no vector in %a",filename) - return - end + binary=decrypt(binary,55665,4) + local names={} local encoding=lpegmatch(p_filterencoding,ascii) - return vector,encoding + local glyphs={} + routines,vector,chars={},{},{} + if shapestoo then + lpegmatch(p_filterroutines,binary) + lpegmatch(p_filtershapes,binary) + local data={ + dictionaries={ + { + charstrings=chars, + charset=vector, + subroutines=routines, + } + }, + } + fonts.handlers.otf.readers.parsecharstrings(data,glyphs,true,true) + else + lpegmatch(p_filternames,binary) + end + names=vector + routines,vector,chars=nil,nil,nil + return names,encoding,glyphs end local pfb=handlers.pfb or {} handlers.pfb=pfb @@ -24326,6 +24383,10 @@ do end end end + get_shapes=function(pfbname) + local vector,encoding,glyphs=loadpfbvector(pfbname,true) + return glyphs + end end local spacer=patterns.spacer local whitespace=patterns.whitespace @@ -24474,6 +24535,22 @@ function readers.loadfont(afmname,pfbname) return data end end +function readers.loadshapes(filename) + local fullname=resolvers.findfile(filename) or "" + if fullname=="" then + return { + filename="not found: "..filename, + glyphs={} + } + else + return { + filename=fullname, + format="opentype", + glyphs=get_shapes(fullname) or {}, + units=1000, + } + end +end function readers.getinfo(filename) local data=read(resolvers.findfile(filename),infoparser) if data then -- cgit v1.2.3