diff options
Diffstat (limited to 'Master/texmf-dist/tex/luatex')
56 files changed, 6094 insertions, 2252 deletions
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-2021-05-20.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-2022-02-24.lua index f5416680997..aee123be032 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-2021-05-20.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-2022-02-24.lua @@ -1,9 +1,9 @@ --[[info----------------------------------------------------------------------- Luaotfload fontloader package - build 2021-05-20 19:43:52 + build 2022-02-24 08:04:22 ------------------------------------------------------------------------------- - © 2021 PRAGMA ADE / ConTeXt Development Team + © 2022 PRAGMA ADE / ConTeXt Development Team The code in this file is provided under the GPL v2.0 license. See the file COPYING in the Luaotfload repository for details. @@ -63,7 +63,7 @@ --info]]----------------------------------------------------------------------- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “data-con” 23a0f5386d46a1c157ae82a373e35fa0] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “data-con” 23a0f5386d46a1c157ae82a373e35fa0] --- if not modules then modules={} end modules ['data-con']={ version=1.100, @@ -184,10 +184,10 @@ function containers.cleanname(name) return (gsub(lower(name),"[^%w\128-\255]+","-")) end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “data-con”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “data-con”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “basics-nod” b2e0a0e5e1e27349aed464fa738b8db1] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “basics-nod” 1750af43148def9e7a75c6900a96f9dc] --- if not modules then modules={} end modules ['luatex-fonts-nod']={ version=1.001, @@ -276,7 +276,7 @@ nuts.setcomponents=direct.setcomponents nuts.setdirection=direct.setdirection nuts.setdisc=direct.setdisc nuts.setreplace=direct.setreplace -nuts.setfield=setfield +nuts.setfield=direct.setfield nuts.setkern=direct.setkern nuts.setlink=direct.setlink nuts.setlist=direct.setlist @@ -454,10 +454,10 @@ do end end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “basics-nod”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “basics-nod”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “basics-chr” 64fafe4eaf55d64aead6513c1e74a024] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “basics-chr” 1bb1ea05b5ca1bb5a9848b9eb66322bf] --- characters=characters or {} @@ -660,6 +660,7 @@ characters.classifiers={ [1562]=5, [1563]=6, [1564]=6, + [1565]=6, [1566]=6, [1567]=6, [1568]=2, @@ -1126,9 +1127,9 @@ characters.classifiers={ [2131]=2, [2132]=3, [2133]=2, - [2134]=4, - [2135]=4, - [2136]=4, + [2134]=3, + [2135]=3, + [2136]=3, [2137]=5, [2138]=5, [2139]=5, @@ -1143,6 +1144,47 @@ characters.classifiers={ [2152]=2, [2153]=3, [2154]=3, + [2160]=3, + [2161]=3, + [2162]=3, + [2163]=3, + [2164]=3, + [2165]=3, + [2166]=3, + [2167]=3, + [2168]=3, + [2169]=3, + [2170]=3, + [2171]=3, + [2172]=3, + [2173]=3, + [2174]=3, + [2175]=3, + [2176]=3, + [2177]=3, + [2178]=3, + [2179]=2, + [2180]=2, + [2181]=2, + [2182]=2, + [2183]=4, + [2184]=4, + [2185]=2, + [2186]=2, + [2187]=2, + [2188]=2, + [2189]=2, + [2190]=3, + [2192]=4, + [2193]=4, + [2200]=5, + [2201]=5, + [2202]=5, + [2203]=5, + [2204]=5, + [2205]=5, + [2206]=5, + [2207]=5, [2208]=2, [2209]=2, [2210]=2, @@ -1164,6 +1206,7 @@ characters.classifiers={ [2226]=3, [2227]=2, [2228]=2, + [2229]=2, [2230]=2, [2231]=2, [2232]=2, @@ -1172,6 +1215,26 @@ characters.classifiers={ [2235]=2, [2236]=2, [2237]=2, + [2238]=2, + [2239]=2, + [2240]=2, + [2241]=2, + [2242]=2, + [2243]=2, + [2244]=2, + [2245]=2, + [2246]=2, + [2247]=2, + [2248]=2, + [2250]=5, + [2251]=5, + [2252]=5, + [2253]=5, + [2254]=5, + [2255]=5, + [2256]=5, + [2257]=5, + [2258]=5, [2259]=5, [2260]=5, [2261]=5, @@ -1291,6 +1354,7 @@ characters.classifiers={ [2883]=5, [2884]=5, [2893]=5, + [2901]=5, [2902]=5, [2914]=5, [2915]=5, @@ -1299,6 +1363,7 @@ characters.classifiers={ [3021]=5, [3072]=5, [3076]=5, + [3132]=5, [3134]=5, [3135]=5, [3136]=5, @@ -1332,6 +1397,7 @@ characters.classifiers={ [3405]=5, [3426]=5, [3427]=5, + [3457]=5, [3530]=5, [3538]=5, [3539]=5, @@ -1511,6 +1577,7 @@ characters.classifiers={ [6156]=5, [6157]=5, [6158]=4, + [6159]=5, [6176]=2, [6177]=2, [6178]=2, @@ -1695,6 +1762,22 @@ characters.classifiers={ [6843]=5, [6844]=5, [6845]=5, + [6847]=5, + [6848]=5, + [6849]=5, + [6850]=5, + [6851]=5, + [6852]=5, + [6853]=5, + [6854]=5, + [6855]=5, + [6856]=5, + [6857]=5, + [6858]=5, + [6859]=5, + [6860]=5, + [6861]=5, + [6862]=5, [6912]=5, [6913]=5, [6914]=5, @@ -1827,6 +1910,7 @@ characters.classifiers={ [7671]=5, [7672]=5, [7673]=5, + [7674]=5, [7675]=5, [7676]=5, [7677]=5, @@ -1928,6 +2012,7 @@ characters.classifiers={ [43019]=5, [43045]=5, [43046]=5, + [43052]=5, [43072]=2, [43073]=2, [43074]=2, @@ -2204,6 +2289,8 @@ characters.classifiers={ [68901]=5, [68902]=5, [68903]=5, + [69291]=5, + [69292]=5, [69424]=2, [69425]=2, [69426]=2, @@ -2241,6 +2328,56 @@ characters.classifiers={ [69458]=2, [69459]=2, [69460]=3, + [69488]=2, + [69489]=2, + [69490]=2, + [69491]=2, + [69492]=3, + [69493]=3, + [69494]=2, + [69495]=2, + [69496]=2, + [69497]=2, + [69498]=2, + [69499]=2, + [69500]=2, + [69501]=2, + [69502]=2, + [69503]=2, + [69504]=2, + [69505]=2, + [69506]=5, + [69507]=5, + [69508]=5, + [69509]=5, + [69552]=2, + [69553]=4, + [69554]=2, + [69555]=2, + [69556]=3, + [69557]=3, + [69558]=3, + [69559]=4, + [69560]=2, + [69561]=3, + [69562]=3, + [69563]=2, + [69564]=2, + [69565]=3, + [69566]=2, + [69567]=2, + [69568]=4, + [69569]=2, + [69570]=3, + [69571]=3, + [69572]=2, + [69573]=4, + [69574]=4, + [69575]=4, + [69576]=4, + [69577]=3, + [69578]=2, + [69579]=1, [69633]=5, [69688]=5, [69689]=5, @@ -2257,6 +2394,9 @@ characters.classifiers={ [69700]=5, [69701]=5, [69702]=5, + [69744]=5, + [69747]=5, + [69748]=5, [69759]=5, [69760]=5, [69761]=5, @@ -2267,6 +2407,7 @@ characters.classifiers={ [69817]=5, [69818]=5, [69821]=4, + [69826]=5, [69837]=4, [69888]=5, [69889]=5, @@ -2299,6 +2440,7 @@ characters.classifiers={ [70090]=5, [70091]=5, [70092]=5, + [70095]=5, [70191]=5, [70192]=5, [70193]=5, @@ -2409,6 +2551,10 @@ characters.classifiers={ [71735]=5, [71737]=5, [71738]=5, + [71995]=5, + [71996]=5, + [71998]=5, + [72003]=5, [72148]=5, [72149]=5, [72150]=5, @@ -2546,8 +2692,78 @@ characters.classifiers={ [94096]=5, [94097]=5, [94098]=5, + [94180]=5, [113821]=5, [113822]=5, + [118528]=5, + [118529]=5, + [118530]=5, + [118531]=5, + [118532]=5, + [118533]=5, + [118534]=5, + [118535]=5, + [118536]=5, + [118537]=5, + [118538]=5, + [118539]=5, + [118540]=5, + [118541]=5, + [118542]=5, + [118543]=5, + [118544]=5, + [118545]=5, + [118546]=5, + [118547]=5, + [118548]=5, + [118549]=5, + [118550]=5, + [118551]=5, + [118552]=5, + [118553]=5, + [118554]=5, + [118555]=5, + [118556]=5, + [118557]=5, + [118558]=5, + [118559]=5, + [118560]=5, + [118561]=5, + [118562]=5, + [118563]=5, + [118564]=5, + [118565]=5, + [118566]=5, + [118567]=5, + [118568]=5, + [118569]=5, + [118570]=5, + [118571]=5, + [118572]=5, + [118573]=5, + [118576]=5, + [118577]=5, + [118578]=5, + [118579]=5, + [118580]=5, + [118581]=5, + [118582]=5, + [118583]=5, + [118584]=5, + [118585]=5, + [118586]=5, + [118587]=5, + [118588]=5, + [118589]=5, + [118590]=5, + [118591]=5, + [118592]=5, + [118593]=5, + [118594]=5, + [118595]=5, + [118596]=5, + [118597]=5, + [118598]=5, [119143]=5, [119144]=5, [119145]=5, @@ -2745,6 +2961,7 @@ characters.classifiers={ [123188]=5, [123189]=5, [123190]=5, + [123566]=5, [123628]=5, [123629]=5, [123630]=5, @@ -2912,6 +3129,7 @@ characters.indicgroups={ [2879]=true, [2902]=true, [3376]=true, + [5901]=true, }, ["after_postscript"]={ [2433]=true, @@ -3087,6 +3305,7 @@ characters.indicgroups={ [2915]=true, [3009]=true, [3010]=true, + [3132]=true, [3170]=true, [3171]=true, [3260]=true, @@ -3505,6 +3724,7 @@ characters.indicgroups={ [4225]=true, [4226]=true, [4238]=true, + [5901]=true, [43488]=true, [43489]=true, [43490]=true, @@ -3829,6 +4049,7 @@ characters.indicgroups={ [3090]=true, [3091]=true, [3092]=true, + [3165]=true, [3168]=true, [3169]=true, [3205]=true, @@ -3845,6 +4066,7 @@ characters.indicgroups={ [3218]=true, [3219]=true, [3220]=true, + [3293]=true, [3296]=true, [3297]=true, [3333]=true, @@ -3885,6 +4107,7 @@ characters.indicgroups={ [2620]=true, [2748]=true, [2876]=true, + [3132]=true, [3260]=true, }, ["post_mark"]={ @@ -3988,6 +4211,7 @@ characters.indicgroups={ [3120]=true, [3248]=true, [3376]=true, + [5901]=true, }, ["stress_tone_mark"]={ [2385]=true, @@ -4067,10 +4291,10 @@ characters.indicgroups={ }, } -end --- [luaotfload, fontloader-2021-05-20.lua scope for “basics-chr”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “basics-chr”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-ini” 5343de60613adb24cdd7a8d2ee1d153c] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-ini” 5343de60613adb24cdd7a8d2ee1d153c] --- if not modules then modules={} end modules ['font-ini']={ version=1.001, @@ -4111,10 +4335,10 @@ if node and not tex.getfontoffamily then tex.getfontoffamily=node.family_font end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-ini”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-ini”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “fonts-mis” 17e967c9ec4d001deefd43ddf25e98f7] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “fonts-mis” 17e967c9ec4d001deefd43ddf25e98f7] --- if not modules then modules={} end modules ['luatex-font-mis']={ version=1.001, @@ -4146,10 +4370,10 @@ function font.each() return table.sortedhash(fonts.hashes.identifiers) end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “fonts-mis”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “fonts-mis”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-con” 724e5dd14aec2d4d189f8619898004cf] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-con” 724e5dd14aec2d4d189f8619898004cf] --- if not modules then modules={} end modules ['font-con']={ version=1.001, @@ -5517,10 +5741,10 @@ function constructors.addcoreunicodes(unicodes) return unicodes end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-con”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-con”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “fonts-enc” 5ff4ca50493d7c4ecea0e15c203099f0] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “fonts-enc” c24fd6b4f34f87b9c4ff5e6193ce2656] --- if not modules then modules={} end modules ['luatex-font-enc']={ version=1.001, @@ -5537,10 +5761,16 @@ local encodings={} fonts.encodings=encodings encodings.agl={} encodings.known={} +encodings.glyphlistfilename="font-age.lua" setmetatable(encodings.agl,{ __index=function(t,k) if k=="unicodes" then logs.report("fonts","loading (extended) adobe glyph list") - local unicodes=dofile(resolvers.findfile("font-age.lua")) + local foundname=resolvers.findfile(encodings.glyphlistfilename) or "" + local unicodes=foundname~="" and dofile(foundname) + if type(unicodes)~="table" then + logs.report("fonts","missing or invalid (extended) adobe glyph list") + unicodes={} + end encodings.agl={ unicodes=unicodes } return unicodes else @@ -5589,10 +5819,10 @@ function encodings.load(filename) return containers.write(encodings.cache,name,data) end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “fonts-enc”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “fonts-enc”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-cid” 22b0367742fb253deef84ef7ccf5e8de] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-cid” 22b0367742fb253deef84ef7ccf5e8de] --- if not modules then modules={} end modules ['font-cid']={ version=1.001, @@ -5743,10 +5973,10 @@ function cid.getmap(specification) return found end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-cid”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-cid”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-map” c4a39db5ad822b348aa61bca5f4a599a] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-map” c4a39db5ad822b348aa61bca5f4a599a] --- if not modules then modules={} end modules ['font-map']={ version=1.001, @@ -6154,10 +6384,10 @@ function mappings.addtounicode(data,filename,checklookups,forceligatures) end end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-map”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-map”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-vfc” a81c29eda98cd62cbebdb6c93544b50d] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-vfc” a81c29eda98cd62cbebdb6c93544b50d] --- if not modules then modules={} end modules ['font-vfc']={ version=1.001, @@ -6254,10 +6484,10 @@ helpers.commands=utilities.storage.allocate { dummy=dummy, } -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-vfc”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-vfc”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-otr” ad238624c9ebd488d4bbe9af3b620189] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-otr” 91f70c1216e60227e1437b6c8dcd8647] --- if not modules then modules={} end modules ['font-otr']={ version=1.001, @@ -6499,15 +6729,15 @@ local weights={ [900]="black", } local widths={ - [1]="ultracondensed", - [2]="extracondensed", - [3]="condensed", - [4]="semicondensed", - [5]="normal", - [6]="semiexpanded", - [7]="expanded", - [8]="extraexpanded", - [9]="ultraexpanded", + "ultracondensed", + "extracondensed", + "condensed", + "semicondensed", + "normal", + "semiexpanded", + "expanded", + "extraexpanded", + "ultraexpanded", } setmetatableindex(weights,function(t,k) local r=floor((k+50)/100)*100 @@ -6517,30 +6747,28 @@ end) setmetatableindex(widths,function(t,k) return "normal" end) -local panoseweights={ - [ 0]="normal", - [ 1]="normal", - [ 2]="verylight", - [ 3]="light", - [ 4]="thin", - [ 5]="book", - [ 6]="medium", - [ 7]="demi", - [ 8]="bold", - [ 9]="heavy", - [10]="black", +local panoseweights={ [0]="normal", + "normal", + "verylight", + "light", + "thin", + "book", + "medium", + "demi", + "bold", + "heavy", + "black", } -local panosewidths={ - [ 0]="normal", - [ 1]="normal", - [ 2]="normal", - [ 3]="normal", - [ 4]="normal", - [ 5]="expanded", - [ 6]="condensed", - [ 7]="veryexpanded", - [ 8]="verycondensed", - [ 9]="monospaced", +local panosewidths={ [0]="normal", + "normal", + "normal", + "normal", + "normal", + "expanded", + "condensed", + "veryexpanded", + "verycondensed", + "monospaced", } local helpers={} readers.helpers=helpers @@ -6974,7 +7202,7 @@ readers.post=function(f,fontdata,specification) local version=readulong(f) fontdata.postscript={ version=version, - italicangle=round(1000*readfixed(f))/1000, + italicangle=readfixed(f), underlineposition=readfword(f), underlinethickness=readfword(f), monospaced=readulong(f), @@ -7591,6 +7819,7 @@ local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo,inst pfmwidth=metrics.widthclass or 5, panosewidth=metrics.panosewidth, panoseweight=metrics.panoseweight, + fstype=metrics.fstype or 0, italicangle=postscript.italicangle or 0, units=fontheader.units or 0, designsize=fontdata.designsize, @@ -8084,6 +8313,9 @@ end function readers.compact(fontdata) report("the %a helper is not yet implemented","compact") end +function readers.condense(fontdata) + report("the %a helper is not yet implemented","condense") +end local extenders={} function readers.registerextender(extender) extenders[#extenders+1]=extender @@ -8099,10 +8331,10 @@ function readers.extend(fontdata) end end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-otr”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-otr”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-oti” 309a75f9c14b77d87e94eba827dc4e71] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-oti” 309a75f9c14b77d87e94eba827dc4e71] --- if not modules then modules={} end modules ['font-oti']={ version=1.001, @@ -8245,10 +8477,10 @@ function otffeatures.checkeddefaultlanguage(featuretype,autolanguage,languages) end end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-oti”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-oti”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-ott” 3f67b1b1d40bbc222681d8db4aff0f42] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-ott” 3f67b1b1d40bbc222681d8db4aff0f42] --- if not modules then modules={} end modules ["font-ott"]={ version=1.001, @@ -9364,10 +9596,10 @@ function otffeatures.normalize(features,wrap) end end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-ott”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-ott”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-cff” 01e7c6fbc8961a566f4e4f8cbedb7004] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-cff” e38c1a40e1d1d577cdf7920fa50d0654] --- if not modules then modules={} end modules ['font-cff']={ version=1.001, @@ -9817,7 +10049,8 @@ do top=0 end local p_dictionary=( - p_byte+p_positive+p_negative+p_short+p_long+p_nibbles+p_single+p_double+p_unsupported + p_byte+p_positive+p_negative+p_short+p_long+p_nibbles+p_single+p_double ++p_unsupported )^1 parsedictionaries=function(data,dictionaries,version) stack={} @@ -9889,6 +10122,7 @@ do local y=0 local width=false local lsb=0 +local result={} local r=0 local stems=0 local globalbias=0 @@ -10843,12 +11077,12 @@ do stack[top]=-t*256+64148-tab[i+1] i=i+2 else - local n=0x100*tab[i+1]+tab[i+2] - if n>=0x8000 then - stack[top]=n-0x10000+(0x100*tab[i+3]+tab[i+4])/0xFFFF - else - stack[top]=n+(0x100*tab[i+3]+tab[i+4])/0xFFFF + local n1=0x100*tab[i+1]+tab[i+2] + local n2=0x100*tab[i+3]+tab[i+4] + if n1>=0x8000 then + n1=n1-0x10000 end + stack[top]=n1+n2/0xFFFF i=i+5 end elseif t==28 then @@ -10936,7 +11170,7 @@ do i=i+s+1 elseif t==1 or t==3 or t==18 or operation==23 then p_getstem() - if true then + if version=="cff" then if top>0 then for i=1,top do r=r+1;result[r]=encode[stack[i]] @@ -10949,7 +11183,7 @@ do end i=i+1 elseif t==19 or t==20 then - local s=p_getmask() or 0 + local s=p_getmask() or 0 if true then if top>0 then for i=1,top do @@ -10972,7 +11206,7 @@ do i=i+1 elseif t==13 then hsbw() - if version=="cff" then + if true then r=r+1;result[r]=encode[lsb] r=r+1;result[r]=chars[22] else @@ -11099,6 +11333,7 @@ do r=r+1 result[r]=c_endchar local stream=concat(result) +result=nil if glyph then glyph.stream=stream else @@ -11120,6 +11355,7 @@ do width=width, name=charset and charset[index] or nil, } +result=nil else glyphs[index]={ boundingbox=boundingbox, @@ -11592,10 +11828,10 @@ function readers.cffcheck(filename) end end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-cff”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-cff”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-ttf” ffffc827e1bcddc33a2b615340ecff7f] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-ttf” feba1fdc80160343b24f89ef978e53cb] --- if not modules then modules={} end modules ['font-ttf']={ version=1.001, @@ -11607,7 +11843,7 @@ if not modules then modules={} end modules ['font-ttf']={ } local next,type,unpack=next,type,unpack local band,rshift=bit32.band,bit32.rshift -local sqrt,round=math.sqrt,math.round +local sqrt,round,abs,min,max=math.sqrt,math.round,math.abs,math.min,math.max local char,rep=string.char,string.rep local concat=table.concat local idiv=number.idiv @@ -11741,69 +11977,164 @@ local function curveto(m_x,m_y,l_x,l_y,r_x,r_y) r_x+2/3*(m_x-r_x),r_y+2/3*(m_y-r_y), r_x,r_y,"c" end +local xv={} +local yv={} local function applyaxis(glyph,shape,deltas,dowidth) local points=shape.points if points then local nofpoints=#points - local h=nofpoints+2 - local l=nofpoints+1 local dw=0 local dl=0 for i=1,#deltas do local deltaset=deltas[i] local xvalues=deltaset.xvalues local yvalues=deltaset.yvalues - local dpoints=deltaset.points - local factor=deltaset.factor - if dpoints then - local nofdpoints=#dpoints - for i=1,nofdpoints do - local d=dpoints[i] - local p=points[d] - if p then - if xvalues then - local x=xvalues[i] - if x and x~=0 then - p[1]=p[1]+factor*x + if xvalues and yvalues then + local dpoints=deltaset.points + local factor=deltaset.factor + if dpoints then + local cnt=#dpoints + if dowidth then + cnt=cnt-4 + end + if cnt==1 then + local d=dpoints[1] + local x=xvalues[d]*factor + local y=yvalues[d]*factor + for i=1,nofpoints do + local p=points[i] + if x~=0 then + p[1]=p[1]+x + end + if y~=0 then + p[2]=p[2]+y end end - if yvalues then - local y=yvalues[i] - if y and y~=0 then - p[2]=p[2]+factor*y + elseif cnt>0 then + local function find(i) + local prv=cnt + for j=1,cnt do + local nxt=dpoints[j] + if nxt==i then + return false,j,false + elseif nxt>i then + return prv,false,j + end + prv=j + end + return prv,false,1 + end + for i=1,nofpoints do + local d1,d2,d3=find(i) + local p2=points[i] + if d2 then + xv[i]=xvalues[d2] + yv[i]=yvalues[d2] + else + local n1=dpoints[d1] + local n3=dpoints[d3] + if n1>nofpoints then + n1=nofpoints + end + if n3>nofpoints then + n3=nofpoints + end + local p1=points[n1] + local p3=points[n3] + local p1x=p1[1] + local p2x=p2[1] + local p3x=p3[1] + local p1y=p1[2] + local p2y=p2[2] + local p3y=p3[2] + local x1=xvalues[d1] + local y1=yvalues[d1] + local x3=xvalues[d3] + local y3=yvalues[d3] + local fx + local fy + if p1x==p3x then + if x1==x3 then + fx=x1 + else + fx=0 + end + elseif p2x<=min(p1x,p3x) then + if p1x<p3x then + fx=x1 + else + fx=x3 + end + elseif p2x>=max(p1x,p3x) then + if p1x>p3x then + fx=x1 + else + fx=x3 + end + else + fx=(p2x-p1x)/(p3x-p1x) + fx=(1-fx)*x1+fx*x3 + end + if p1y==p3y then + if y1==y3 then + fy=y1 + else + fy=0 + end + elseif p2y<=min(p1y,p3y) then + if p1y<p3y then + fy=y1 + else + fy=y3 + end + elseif p2y>=max(p1y,p3y) then + if p1y>p3y then + fy=y1 + else + fy=y3 + end + else + fy=(p2y-p1y)/(p3y-p1y) + fy=(1-fy)*y1+fy*y3 + end + xv[i]=fx + yv[i]=fy end end - elseif dowidth then - if d==h then - local x=xvalues[i] - if x then - dw=dw+factor*x + for i=1,nofpoints do + local pi=points[i] + local fx=xv[i] + local fy=yv[i] + if fx~=0 then + pi[1]=pi[1]+factor*fx end - elseif d==l then - local x=xvalues[i] - if x then - dl=dl+factor*x + if fy~=0 then + pi[2]=pi[2]+factor*fy end end + else + report("bad deltapoint data, maybe a missing hvar table") end - end - else - for i=1,nofpoints do - local p=points[i] - if xvalues then + else + for i=1,nofpoints do + local p=points[i] local x=xvalues[i] - if x and x~=0 then - p[1]=p[1]+factor*x - end - end - if yvalues then - local y=yvalues[i] - if y and y~=0 then - p[2]=p[2]+factor*y + if x then + local y=yvalues[i] + if x~=0 then + p[1]=p[1]+factor*x + end + if y~=0 then + p[2]=p[2]+factor*y + end + else + break end end end if dowidth then + local h=nofpoints+2 + local l=nofpoints+1 local x=xvalues[h] if x then dw=dw+factor*x @@ -12382,13 +12713,13 @@ local function readcomposite(f) yoffset=yoffset*yscale end elseif band(flags,0x0080)~=0 then - xscale=read2dot14(f) - xrotate=read2dot14(f) - yrotate=read2dot14(f) - yscale=read2dot14(f) + xscale=read2dot14(f) + xrotate=read2dot14(f) + yrotate=read2dot14(f) + yscale=read2dot14(f) if f_xyarg and f_offset then - xoffset=xoffset*sqrt(xscale^2+xrotate^2) - yoffset=yoffset*sqrt(yrotate^2+yscale^2) + xoffset=xoffset*sqrt(xscale^2+yrotate^2) + yoffset=yoffset*sqrt(xrotate^2+yscale^2) end end nofcomponents=nofcomponents+1 @@ -12546,40 +12877,6 @@ end local function readdeltas(f,nofpoints) local deltas={} local p=0 - local z=0 - while nofpoints>0 do - local control=readbyte(f) -if not control then - break -end - local allzero=band(control,0x80)~=0 - local runlength=band(control,0x3F)+1 - if allzero then - z=z+runlength - else - local runreader=band(control,0x40)~=0 and readshort or readinteger - if z>0 then - for i=1,z do - p=p+1 - deltas[p]=0 - end - z=0 - end - for i=1,runlength do - p=p+1 - deltas[p]=runreader(f) - end - end - nofpoints=nofpoints-runlength - end - if p>0 then - return deltas - else - end -end -local function readdeltas(f,nofpoints) - local deltas={} - local p=0 while nofpoints>0 do local control=readbyte(f) if control then @@ -12766,10 +13063,10 @@ function readers.gvar(f,fontdata,specification,glyphdata,shapedata) end end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-ttf”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-ttf”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-dsp” 174ce11f3ba0bddf270f48d8d2addd56] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-dsp” 7616dae672693fd8ec0f1892358019a2] --- if not modules then modules={} end modules ['font-dsp']={ version=1.001, @@ -15938,6 +16235,7 @@ function readers.hvar(f,fontdata,specification) end local tableoffset=gotodatatable(f,fontdata,"hvar",specification.variable) if not tableoffset then + report("no hvar table, expect problems due to messy widths") return end local version=readulong(f) @@ -16045,10 +16343,10 @@ function readers.mvar(f,fontdata,specification) end end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-dsp”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-dsp”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-oup” 4eb2f26e51835239e26a8df2ecf04366] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-oup” e6e2cc0a202aec2c787d5524e64d340a] --- if not modules then modules={} end modules ['font-oup']={ version=1.001, @@ -16083,9 +16381,9 @@ local f_index=formatters["I%05X"] local f_character_y=formatters["%C"] local f_character_n=formatters["[ %C ]"] local check_duplicates=true -local check_soft_hyphen=true +local check_soft_hyphen=context directives.register("otf.checksofthyphen",function(v) - check_soft_hyphen=v + check_soft_hyphen=v end) local function replaced(list,index,replacement) if type(list)=="number" then @@ -16474,27 +16772,30 @@ local function copyduplicates(fontdata) local resources=fontdata.resources local duplicates=resources.duplicates if check_soft_hyphen then - local ds=descriptions[0xAD] - if not ds or ds.width==0 then - if ds then + local dh=descriptions[0x2D] + if dh then + local ds=descriptions[0xAD] + if not ds or ds.width~=dh.width then descriptions[0xAD]=nil - if trace_unicodes then - report_unicodes("patching soft hyphen") + if ds then + if trace_unicodes then + report_unicodes("patching soft hyphen") + end + else + if trace_unicodes then + report_unicodes("adding soft hyphen") + end end - else - if trace_unicodes then - report_unicodes("adding soft hyphen") + if not duplicates then + duplicates={} + resources.duplicates=duplicates + end + local d=duplicates[0x2D] + if d then + d[0xAD]=true + else + duplicates[0x2D]={ [0xAD]=true } end - end - if not duplicates then - duplicates={} - resources.duplicates=duplicates - end - local dh=duplicates[0x2D] - if dh then - dh[#dh+1]={ [0xAD]=true } - else - duplicates[0x2D]={ [0xAD]=true } end end end @@ -16760,6 +17061,7 @@ local function unifyglyphs(fontdata,usenames) local resources=fontdata.resources local zero=glyphs[0] local zerocode=zero.unicode + local nofglyphs=#glyphs if not zerocode then zerocode=private zero.unicode=zerocode @@ -16774,7 +17076,7 @@ local function unifyglyphs(fontdata,usenames) indices[0]=zerocode end if names then - for index=1,#glyphs do + for index=1,nofglyphs do local glyph=glyphs[index] local unicode=glyph.unicode if not unicode then @@ -16807,7 +17109,7 @@ local function unifyglyphs(fontdata,usenames) descriptions[unicode]=glyph end elseif trace_unicodes then - for index=1,#glyphs do + for index=1,nofglyphs do local glyph=glyphs[index] local unicode=glyph.unicode if not unicode then @@ -16848,7 +17150,7 @@ local function unifyglyphs(fontdata,usenames) descriptions[unicode]=glyph end else - for index=1,#glyphs do + for index=1,nofglyphs do local glyph=glyphs[index] local unicode=glyph.unicode if not unicode then @@ -16874,7 +17176,7 @@ local function unifyglyphs(fontdata,usenames) descriptions[unicode]=glyph end end - for index=1,#glyphs do + for index=1,nofglyphs do local math=glyphs[index].math if math then local list=math.vparts @@ -16897,7 +17199,7 @@ local function unifyglyphs(fontdata,usenames) end local colorpalettes=resources.colorpalettes if colorpalettes then - for index=1,#glyphs do + for index=1,nofglyphs do local colors=glyphs[index].colors if colors then for i=1,#colors do @@ -16912,6 +17214,7 @@ local function unifyglyphs(fontdata,usenames) fontdata.names=names fontdata.descriptions=descriptions fontdata.hashmethod=hashmethod + fontdata.nofglyphs=nofglyphs return indices,names end local p_crappyname do @@ -16984,6 +17287,10 @@ function readers.getcomponents(fontdata) local function traverse(p,k,v) if k=="ligature" then collected[v]={ unpack(l) } + elseif tonumber(v) then + insert(l,k) + collected[v]={ unpack(l) } + remove(l) else insert(l,k) for k,vv in next,v do @@ -17144,7 +17451,6 @@ local function tabstr_flat(t) end end local function tabstr_mixed(t) - local s={} local n=#t if n==0 then return "" @@ -17158,6 +17464,7 @@ local function tabstr_mixed(t) return tostring(k) end else + local s={} for i=1,n do local k=t[i] if k==true then @@ -18481,6 +18788,85 @@ function readers.compact(data) end end end +if CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 then + local done=0 + local function condense_1(k,v,t) + if type(v)=="table" then + local u=false + local l=false + for k,v in next,v do + if k=="ligature" then + l=v + if u then + break + end + elseif u then + break + else + u=true + end + end + if l and not u then + t[k]=l + done=done+1 + end + if u then + for k,vv in next,v do + if k~="ligature" then + condense_1(k,vv,v) + end + end + end + end + end + local function condensesteps_1(lookup) + done=0 + if lookup.type=="gsub_ligature" then + local steps=lookup.steps + if steps then + for i=1,#steps do + local step=steps[i] + local coverage=step.coverage + if coverage then + for k,v in next,coverage do + if condense_1(k,v,coverage) then + coverage[k]=v.ligature + done=done+1 + end + end + end + end + end + end + return done + end + function readers.condense(data) + if not data or data.condensed then + return + else + data.condensed=true + end + local resources=data.resources + local condensed=0 + local function condense(what) + local lookups=resources[what] + if lookups then + for i=1,#lookups do + condensed=condensed+condensesteps_1(lookups[i]) + end + elseif trace_optimizations then + report_optimizations("no lookups in %a",what) + end + end + condense("sequences") + condense("sublookups") + if trace_optimizations then + if condensed>0 then + report_optimizations("%i ligatures condensed",condensed) + end + end + end +end local function mergesteps(t,k) if k=="merged" then local merged={} @@ -18585,7 +18971,7 @@ function readers.expand(data) end end end - local function expandlookups(sequences) + local function expandlookups(sequences,whatever) if sequences then for i=1,#sequences do local sequence=sequences[i] @@ -18658,6 +19044,9 @@ function readers.expand(data) if lookups then for k,v in next,lookups do local lookup=sublookups[v] +if not lookup and whatever then + lookup=whatever[v] +end if lookup then lookups[k]=lookup if not subtype then @@ -18716,13 +19105,13 @@ function readers.expand(data) end end expandlookups(sequences) - expandlookups(sublookups) + expandlookups(sublookups,sequences) end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-oup”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-oup”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-otl” 6f701653de713e8aa80d82c7cdf32d75] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-otl” 67078f58bc1a612e4646523538a4e3eb] --- if not modules then modules={} end modules ['font-otl']={ version=1.001, @@ -18734,7 +19123,7 @@ if not modules then modules={} end modules ['font-otl']={ local lower=string.lower local type,next,tonumber,tostring,unpack=type,next,tonumber,tostring,unpack local abs=math.abs -local derivetable=table.derive +local derivetable,sortedhash=table.derive,table.sortedhash local formatters=string.formatters local setmetatableindex=table.setmetatableindex local allocate=utilities.storage.allocate @@ -18750,7 +19139,7 @@ local trace_defining=false registertracker("fonts.defining",function(v) trace_d local report_otf=logs.reporter("fonts","otf loading") local fonts=fonts local otf=fonts.handlers.otf -otf.version=3.116 +otf.version=3.119 otf.cache=containers.define("fonts","otl",otf.version,true) otf.svgcache=containers.define("fonts","svg",otf.version,true) otf.pngcache=containers.define("fonts","png",otf.version,true) @@ -18885,6 +19274,9 @@ function otf.load(filename,sub,instance) if cleanup==0 then checkmemory(used,threshold,tracememory) end + if context then + otfreaders.condense(data) + end otfreaders.pack(data) report_otf("loading done") report_otf("saving %a in cache",filename) @@ -18955,7 +19347,7 @@ local function copytotfm(data,cache_id) local properties=derivetable(data.properties) local descriptions=derivetable(data.descriptions) local goodies=derivetable(data.goodies) - local characters={} + local characters={} local parameters={} local mathparameters={} local resources=data.resources @@ -19123,7 +19515,25 @@ local function copytotfm(data,cache_id) properties.name=filename or fullname properties.subfont=subfont if not CONTEXTLMTXMODE or CONTEXTLMTXMODE==0 then - properties.encodingbytes=2 + properties.encodingbytes=2 +elseif CONTEXTLMTXMODE then + local duplicates=resources and resources.duplicates + if duplicates then + local maxindex=data.nofglyphs or metadata.nofglyphs + if maxindex then + for u,d in sortedhash(duplicates) do + local du=descriptions[u] + if du then + for uu in sortedhash(d) do + maxindex=maxindex+1 + descriptions[uu].dupindex=du.index + descriptions[uu].index=maxindex + end + else + end + end + end + end end properties.private=properties.private or data.private or privateoffset return { @@ -19429,10 +19839,10 @@ otf.coverup={ end } -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-otl”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-otl”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-oto” 2b1d6bc63b2ebf8f2655a15e1cda6541] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-oto” 9febc4d75d17a543cd73fb17918f7307] --- if not modules then modules={} end modules ['font-oto']={ version=1.001, @@ -19492,7 +19902,7 @@ local function gref(descriptions,n) end end local function cref(feature,sequence) - return formatters["feature %a, type %a, chain lookup %a"](feature,sequence.type,sequence.name) + return formatters["feature %a, type %a, (chain) lookup %a"](feature,sequence.type,sequence.name) end local function report_substitution(feature,sequence,descriptions,unicode,substitution) if unicode==substitution then @@ -19553,49 +19963,60 @@ end local function makefake(tfmdata,name,present) local private=getprivate(tfmdata) local character={ intermediate=true,ligatures={} } - resources.unicodes[name]=private + tfmdata.resources.unicodes[name]=private tfmdata.characters[private]=character tfmdata.descriptions[private]={ name=name } present[name]=private return character end local function make_1(present,tree,name) - for k,v in next,tree do - if k=="ligature" then - present[name]=v + if tonumber(tree) then + present[name]=v + else + for k,v in next,tree do + if k=="ligature" then + present[name]=v + else + make_1(present,v,name.."_"..k) + end + end + end +end +local function make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,v) + local character=characters[preceding] + if not character then + if trace_baseinit then + report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding) + end + character=makefake(tfmdata,name,present) + end + local ligatures=character.ligatures + if ligatures then + ligatures[unicode]={ char=v } + else + character.ligatures={ [unicode]={ char=v } } + end + if done then + local d=done[name] + if not d then + done[name]={ "dummy",v } else - make_1(present,v,name.."_"..k) + d[#d+1]=v end end end local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done) - for k,v in next,tree do - if k=="ligature" then - local character=characters[preceding] - if not character then - if trace_baseinit then - report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding) - end - character=makefake(tfmdata,name,present) - end - local ligatures=character.ligatures - if ligatures then - ligatures[unicode]={ char=v } + if tonumber(tree) then + make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,tree) + else + for k,v in next,tree do + if k=="ligature" then + make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,v) else - character.ligatures={ [unicode]={ char=v } } - end - if done then - local d=done[name] - if not d then - done[name]={ "dummy",v } - else - d[#d+1]=v - end + local code=present[name] or unicode + local name=name.."_"..k + make_2(present,tfmdata,characters,v,name,code,k,done) end - else - local code=present[name] or unicode - local name=name.."_"..k - make_2(present,tfmdata,characters,v,name,code,k,done) end end end @@ -19890,10 +20311,10 @@ registerotffeature { } otf.basemodeinitializer=featuresinitializer -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-oto”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-oto”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-otj” b2c165bd6771a57633f992d07384b739] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-otj” b2c165bd6771a57633f992d07384b739] --- if not modules then modules={} end modules ['font-otj']={ version=1.001, @@ -21386,10 +21807,10 @@ function injections.handler(head,where) end end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-otj”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-otj”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-ota” 2bb12d708e35bc05443eba791e2554e7] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-ota” 2bb12d708e35bc05443eba791e2554e7] --- if not modules then modules={} end modules ['font-ota']={ version=1.001, @@ -21883,10 +22304,10 @@ directives.register("otf.analyze.useunicodemarks",function(v) analyzers.useunicodemarks=v end) -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-ota”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-ota”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-ots” cb106ae882e8548051f00aac8b7fa862] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-ots” 55316a605d11a1d07d38795224c90a63] --- if not modules then modules={} end modules ['font-ots']={ version=1.001, @@ -21928,6 +22349,7 @@ local trace_compruns=false registertracker("otf.compruns",function(v) trace_com local trace_testruns=false registertracker("otf.testruns",function(v) trace_testruns=v end) local forcediscretionaries=false local forcepairadvance=false +local repeatablemultiples=context or false directives.register("otf.forcediscretionaries",function(v) forcediscretionaries=v end) @@ -22306,15 +22728,47 @@ local function multiple_glyphs(head,start,multiple,skiphash,what,stop) insertnodeafter(head,start,n) start=n end - if what==true then - elseif what>1 then - local m=multiple[nofmultiples] - for i=2,what do - local n=copy_node(start) - resetinjection(n) - setchar(n,m) - insertnodeafter(head,start,n) - start=n + if what~=true and repeatablemultiples then + local kind=type(what) + local m,f,l + if kind=="string" then + local what,n=string.match(what,"^repeat(.-)[:=](%d+)$") + if what=="middle" then + m=tonumber(n) + elseif what=="first" then + f=tonumber(n) + elseif what=="last" then + l=tonumber(n) + end + elseif kind=="table" then + m=what.middle + f=what.first + l=what.last + end + if f or m or l then + if m and m>1 and nofmultiples==3 then + local middle=getnext(first) + for i=2,m do + local n=copynode(middle) + resetinjection(n) + insertnodeafter(head,first,n) + end + end + if f and f>1 then + for i=2,f do + local n=copynode(first) + resetinjection(n) + insertnodeafter(head,first,n) + end + end + if l and l>1 then + for i=2,l do + local n=copynode(start) + resetinjection(n) + insertnodeafter(head,start,n) + start=n + end + end end end end @@ -22402,7 +22856,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip while current do local char=ischar(current,currentfont) if char then - local lg=ligature[char] + local lg=not tonumber(ligature) and ligature[char] if lg then stop=current ligature=lg @@ -22415,14 +22869,14 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip end end if stop then - local lig=ligature.ligature - if lig then + local ligature=tonumber(ligature) or ligature.ligature + if ligature then if trace_ligatures then local stopchar=getchar(stop) - head,start=markstoligature(head,start,stop,lig) + head,start=markstoligature(head,start,stop,ligature) logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(getchar(start))) else - head,start=markstoligature(head,start,stop,lig) + head,start=markstoligature(head,start,stop,ligature) end return head,start,true,false else @@ -22437,7 +22891,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip if skiphash and skiphash[char] then current=getnext(current) else - local lg=ligature[char] + local lg=not tonumber(ligature) and ligature[char] if lg then if marks[char] then hasmarks=true @@ -22463,20 +22917,20 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip local match if replace then local char=ischar(replace,currentfont) - if char and ligature[char] then + if char and (not tonumber(ligature) and ligature[char]) then match=true end end if not match and pre then local char=ischar(pre,currentfont) - if char and ligature[char] then + if char and (not tonumber(ligature) and ligature[char]) then match=true end end if not match and not pre or not replace then local n=getnext(discfound) local char=ischar(n,currentfont) - if char and ligature[char] then + if char and (not tonumber(ligature) and ligature[char]) then match=true end end @@ -22519,21 +22973,21 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip return head,start,true,true end end - local lig=ligature.ligature - if lig then + local ligature=tonumber(ligature) or ligature.ligature + if ligature then if stop then if trace_ligatures then local stopchar=getchar(stop) - head,start=toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks) - logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(lig)) + head,start=toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks) + logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(ligature)) else - head,start=toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks) + head,start=toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks) end else resetinjection(start) - setchar(start,lig) + setchar(start,ligature) if trace_ligatures then - logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(lig)) + logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(ligature)) end end return head,start,true,false @@ -22728,7 +23182,7 @@ function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlm end end elseif trace_bugs then - onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + onetimemessage(currentfont,basechar,"no base anchors") end elseif trace_bugs then logwarning("%s: prev node is no char, case %i",pref(dataset,sequence),1) @@ -23007,7 +23461,7 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup if skiphash and skiphash[schar] then current=getnext(current) else - local lg=ligatures[schar] + local lg=not tonumber(ligatures) and ligatures[schar] if lg then ligatures=lg last=current @@ -23026,7 +23480,7 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup end end end - local ligature=ligatures.ligature + local ligature=tonumber(ligatures) or ligatures.ligature if ligature then if chainindex then stop=last @@ -23368,7 +23822,7 @@ function chainprocs.gpos_cursive(head,start,stop,dataset,sequence,currentlookup, end end elseif trace_bugs then - onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) + onetimemessage(currentfont,startchar,"no entry anchors") end break end @@ -23438,7 +23892,7 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) local chainproc=chainprocs[chainkind] if chainproc then local ok - head,start,ok=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash) + head,start,ok=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,1) if ok then done=true end @@ -23855,6 +24309,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s local ck=contexts[k] local seq=ck[3] local f=ck[4] +local last=start if not startchar or not seq[f][startchar] then goto next end @@ -23863,7 +24318,6 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s else local l=ck[5] local current=start - local last=start if l>f then local discfound local n=f+1 @@ -24647,7 +25101,7 @@ local function t_run_single(start,stop,font,attr,lookupcache) while s do local char=ischar(s,font) if char then - local lg=lookupmatch[char] + local lg=not tonumber(lookupmatch) and lookupmatch[char] if lg then if sstop then d=1 @@ -24677,7 +25131,7 @@ local function t_run_single(start,stop,font,attr,lookupcache) break end end - if l and l.ligature then + if l and (tonumber(l) or l.ligature) then lastd=d end else @@ -24802,7 +25256,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps) while s do local char=ischar(s) if char then - local lg=lookupmatch[char] + local lg=not tonumber(lookupmatch) and lookupmatch[char] if lg then if sstop then d=1 @@ -24832,7 +25286,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps) break end end - if l and l.ligature then + if l and (tonumber(l) or l.ligature) then lastd=d end end @@ -25513,10 +25967,10 @@ registerotffeature { }, } -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-ots”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-ots”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-osd” 0e051ca0d3b1a8e0604ced27e753fde8] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-osd” a67b6fb465e7a8b9ecf7c6bbf8dfda03] --- if not modules then modules={} end modules ['font-osd']={ version=1.001, @@ -25525,8 +25979,10 @@ if not modules then modules={} end modules ['font-osd']={ copyright="TAT Zetwerk / PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local insert,imerge,copy,tohash=table.insert,table.imerge,table.copy,table.tohash -local next,type=next,type +local insert,remove,imerge,copy,tohash=table.insert,table.remove,table.imerge,table.copy,table.tohash +local next,type,rawget=next,type,rawget +local formatters=string.formatters +local settings_to_hash=utilities.parsers.settings_to_hash local report=logs.reporter("otf","devanagari") fonts=fonts or {} fonts.analyzers=fonts.analyzers or {} @@ -25536,6 +25992,7 @@ local handlers=otf.handlers local methods=fonts.analyzers.methods local otffeatures=fonts.constructors.features.otf local registerotffeature=otffeatures.register +local trace_steps=false local nuts=nodes.nuts local getnext=nuts.getnext local getprev=nuts.getprev @@ -25561,7 +26018,8 @@ local flushnode=nuts.flushnode local copyinjection=nodes.injections.copy local unsetvalue=attributes.unsetvalue local fontdata=fonts.hashes.identifiers -local a_syllabe=attributes.private('syllabe') +local a_syllabe="syllable" +local a_reordered="reordered" local dotted_circle=0x25CC local c_nbsp=0x00A0 local c_zwnj=0x200C @@ -25581,8 +26039,11 @@ replace_all_nbsp=function(head) return replace_all_nbsp(head) end local processcharacters=nil +local logprocess=nil if context then local fontprocesses=fonts.hashes.processes + local tracers=nodes.tracers + local registermessage=(tracers and tracers.steppers.message) or function() end function processcharacters(head,font) local processors=fontprocesses[font] for i=1,#processors do @@ -25590,6 +26051,16 @@ if context then end return head end + trackers.register("otf.steps",function(v) trace_steps=v end) + logprocess=function(str) + if trace_steps then + registermessage("devanagari %s",str) + if trace_steps=="silent" then + return + end + end + report(str) + end else function processcharacters(head,font) local processors=fontdata[font].shared.processes @@ -25598,6 +26069,8 @@ else end return head end + logprocess=function(str) + end end local indicgroups=characters and characters.indicgroups if not indicgroups and characters then @@ -25707,7 +26180,7 @@ local before_subscript=indicgroups.before_subscript local after_subscript=indicgroups.after_subscript local before_main=indicgroups.before_main local after_main=indicgroups.after_main -local mark_four=table.merged ( +local mark_pre_above_below_post=table.merged ( pre_mark, above_mark, below_mark, @@ -25718,6 +26191,11 @@ local mark_above_below_post=table.merged ( below_mark, post_mark ) +local devanagarihash=table.setmetatableindex(function(t,k) + local v=fontdata[k].resources.devanagari or false + t[k]=v + return v +end) local zw_char={ [c_zwnj]=true, [c_zwj ]=true, @@ -25838,7 +26316,7 @@ local function initializedevanagi(tfmdata) local resources=tfmdata.resources local devanagari=resources.devanagari if not devanagari then - report("adding devanagari features to font") + report("adding features to font") local gsubfeatures=resources.features.gsub local sequences=resources.sequences local sharedfeatures=tfmdata.shared.features @@ -25865,7 +26343,7 @@ local function initializedevanagi(tfmdata) for k,v in next,pre_mark do local locl=coverage[k] if locl then - if #locl>0 then + if #locl>0 then for j=1,#locl do local ck=locl[j] local f=ck[4] @@ -25886,6 +26364,7 @@ local function initializedevanagi(tfmdata) end end end + else end if locl then reorder_matras.steps[1].coverage[locl]=true @@ -25898,7 +26377,7 @@ local function initializedevanagi(tfmdata) if basic_shaping_forms[k] then lastmatch=lastmatch+1 if s~=lastmatch then - table.insert(sequences,lastmatch,table.remove(sequences,s)) + insert(sequences,lastmatch,remove(sequences,s)) end end end @@ -25916,9 +26395,7 @@ local function initializedevanagi(tfmdata) local vatucache={} local pstfcache={} local seqsubset={} - local rephstep={ - coverage={} - } + local rephstep={ coverage={} } local devanagari={ reph=false, vattu=false, @@ -25943,7 +26420,7 @@ local function initializedevanagi(tfmdata) local has_pstf=features.pstf if has_rphf and has_rphf[script] then devanagari.reph=true - elseif (has_blwf and has_blwf[script] ) or (has_vatu and has_vatu[script] ) then + elseif (has_blwf and has_blwf[script]) or (has_vatu and has_vatu[script]) then devanagari.vattu=true for i=1,nofsteps do local step=steps[i] @@ -25951,10 +26428,8 @@ local function initializedevanagi(tfmdata) if coverage then for k,v in next,coverage do for h,w in next,halant do - if v[h] then - if not blwfcache[k] then - blwfcache[k]=v - end + if v[h] and not blwfcache[k] then + blwfcache[k]=v end if has_vatu and has_vatu[script] and not vatucache[k] then vatucache[k]=v @@ -25977,33 +26452,37 @@ local function initializedevanagi(tfmdata) local r=coverage[k] if r then local found=false - if #r>0 then + if #r>0 then for j=1,#r do local ck=r[j] local f=ck[4] local chainlookups=ck[6] - if chainlookups and chainlookups[f] then + if chainlookups then local chainlookup=chainlookups[f] - for j=1,#chainlookup do - local chainstep=chainlookup[j] - local steps=chainstep.steps - local nofsteps=chainstep.nofsteps - for i=1,nofsteps do - local step=steps[i] - local coverage=step.coverage - if coverage then - local h=coverage[k] - if h then - for k,v in next,h do - found=v and v.ligature + if chainlookup then + for j=1,#chainlookup do + local chainstep=chainlookup[j] + local steps=chainstep.steps + local nofsteps=chainstep.nofsteps + for i=1,nofsteps do + local step=steps[i] + local coverage=step.coverage + if coverage then + local h=coverage[k] + if h then + for k,v in next,h do + if v then + found=tonumber(v) or v.ligature + if found then + pre_base_reordering_consonants[found]=true + break + end + end + end if found then - pre_base_reordering_consonants[found]=true break end end - if found then - break - end end end end @@ -26012,10 +26491,12 @@ local function initializedevanagi(tfmdata) end else for k,v in next,r do - found=v and v.ligature - if found then - pre_base_reordering_consonants[found]=true - break + if v then + found=tonumber(v) or v.ligature + if found then + pre_base_reordering_consonants[found]=true + break + end end end end @@ -26033,14 +26514,15 @@ local function initializedevanagi(tfmdata) local step=steps[i] local coverage=step.coverage if coverage then - local reph,rephbase=false,false + local reph=false + local base=false if kind=="rphf" then for k,v in next,ra do local r=coverage[k] if r then - rephbase=k + base=k local h=false - if #r>0 then + if #r>0 then for j=1,#r do local ck=r[j] local f=ck[4] @@ -26060,7 +26542,7 @@ local function initializedevanagi(tfmdata) for k,v in next,halant do local h=r[k] if h then - reph=h.ligature or false + reph=tonumber(h) or h.ligature or false break end end @@ -26077,7 +26559,7 @@ local function initializedevanagi(tfmdata) for k,v in next,halant do local h=r[k] if h then - reph=h.ligature or false + reph=tonumber(h) or h.ligature or false break end end @@ -26088,7 +26570,7 @@ local function initializedevanagi(tfmdata) end end end - seqsubset[#seqsubset+1]={ kind,coverage,reph,rephbase } + seqsubset[#seqsubset+1]={ kind,coverage,reph,base } end end end @@ -26103,7 +26585,7 @@ local function initializedevanagi(tfmdata) local h=coverage[k] if h then local found=false - if #h>0 then + if #h>0 then for j=1,#h do local ck=h[j] local f=ck[4] @@ -26121,10 +26603,12 @@ local function initializedevanagi(tfmdata) local h=coverage[k] if h then for k,v in next,h do - found=v and v.ligature - if found then - pre_base_reordering_consonants[found]=true - break + if v then + found=tonumber(v) or v.ligature + if found then + pre_base_reordering_consonants[found]=true + break + end end end if found then @@ -26138,7 +26622,7 @@ local function initializedevanagi(tfmdata) end else for k,v in next,h do - found=v and v.ligature + found=v and (tonumber(v) or v.ligature) if found then pre_base_reordering_consonants[found]=true break @@ -26164,9 +26648,6 @@ local function initializedevanagi(tfmdata) sharedfeatures["dv03"]=true sharedfeatures["dv04"]=true end - if script=="mlym" or script=="taml" then - devanagari.left_matra_before_base=true - end end end end @@ -26178,6 +26659,44 @@ registerotffeature { node=initializedevanagi, }, } +local function initializeconjuncts(tfmdata,value) + if value then + local resources=tfmdata.resources + local devanagari=resources.devanagari + if devanagari then + local conjuncts="auto" + local movematra="auto" + if type(value)=="string" and value~="auto" then + value=settings_to_hash(value) + conjuncts=rawget(value,"conjuncts") or conjuncts + movematra=rawget(value,"movematra") or movematra + end + if conjuncts=="auto" then + conjuncts="mixed" + end + if movematra=="auto" and + script=="mlym" or + script=="taml" then + movematra="leftbeforebase" + else + movematra="default" + end + devanagari.conjuncts=conjuncts + devanagari.movematra=movematra + if trace_steps then + report("conjuncts %a, movematra %a",conjuncts,movematra) + end + end + end +end +registerotffeature { + name="indic", + description="control indic", + default="auto", + initializers={ + node=initializeconjuncts, + }, +} local show_syntax_errors=false local function inject_syntax_error(head,current,char) local signal=copy_node(current) @@ -26223,36 +26742,40 @@ local function initialize_one(font,attr) end local function contextchain(contexts,n) local char=getchar(n) - for k=1,#contexts do - local ck=contexts[k] - local seq=ck[3] - local f=ck[4] - local l=ck[5] - if (l-f)==1 and seq[f+1][char] then - local ok=true - local c=n - for i=l+1,#seq do - c=getnext(c) - if not c or not seq[i][ischar(c)] then - ok=false - break - end - end - if ok then - c=getprev(n) - for i=1,f-1 do - c=getprev(c) - if not c or not seq[f-i][ischar(c)] then + if not contexts.n then + return contexts[char] + else + for k=1,#contexts do + local ck=contexts[k] + local seq=ck[3] + local f=ck[4] + local l=ck[5] + if (l-f)==1 and seq[f+1][char] then + local ok=true + local c=n + for i=l+1,#seq do + c=getnext(c) + if not c or not seq[i][ischar(c)] then ok=false + break end end - end - if ok then - return true + if ok then + c=getprev(n) + for i=1,f-1 do + c=getprev(c) + if not c or not seq[f-i][ischar(c)] then + ok=false + end + end + end + if ok then + return true + end end end + return false end - return false end local function order_matras(c) local cn=getnext(c) @@ -26281,9 +26804,9 @@ local function order_matras(c) char=getchar(cn) end end +local swapped=table.swapped(states) local function reorder_one(head,start,stop,font,attr,nbspaces) - local reph,vattu,blwfcache,vatucache,pstfcache=initialize_one(font,attr) - local devanagari=fontdata[font].resources.devanagari + local reph,vattu,blwfcache,vatucache,pstfcache=initialize_one(font,attr) local current=start local n=getnext(start) local base=nil @@ -26306,6 +26829,9 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) stop=getprev(stop) head=remove_node(head,current) flushnode(current) + if trace_steps then + logprocess("reorder one, remove nbsp") + end return head,stop,nbspaces else nbspaces=nbspaces+1 @@ -26349,6 +26875,9 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) stop=current end end + if trace_steps then + logprocess("reorder one, handle nbsp") + end end end end @@ -26403,6 +26932,9 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) if lastcons==stop then stop=n end + if trace_steps then + logprocess("reorder one, handle halant") + end end end n=getnext(start) @@ -26427,6 +26959,9 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) if matra==stop then stop=n end + if trace_steps then + logprocess("reorder one, handle matra") + end end local current=start while current~=stop do @@ -26465,14 +27000,19 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) ch=getchar(n) end local tpm=twopart_mark[ch] - while tpm do - local extra=copy_node(n) - copyinjection(extra,n) - ch=tpm[1] - setchar(n,ch) - setchar(extra,tpm[2]) - head=insertnodeafter(head,current,extra) - tpm=twopart_mark[ch] + if tpm then + while tpm do + local extra=copy_node(n) + copyinjection(extra,n) + ch=tpm[1] + setchar(n,ch) + setchar(extra,tpm[2]) + head=insertnodeafter(head,current,extra) + tpm=twopart_mark[ch] + end + if trace_steps then + logprocess("reorder one, handle mark") + end end while c~=stop and dependent_vowel[ch] do c=n @@ -26495,9 +27035,10 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) local bp=getprev(firstcons) local cn=getnext(current) local last=getnext(c) + local done=false while cn~=last do if pre_mark[getchar(cn)] then - if devanagari.left_matra_before_base then + if devanagarihash[font].movematra=="leftbeforebase" then local prev,next=getboth(cn) setlink(prev,next) if cn==stop then @@ -26534,6 +27075,7 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) end cn=next end + done=true elseif current~=base and dependent_vowel[getchar(cn)] then local prev,next=getboth(cn) if next then @@ -26546,6 +27088,7 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) setlink(b,cn,getnext(b)) order_matras(cn) cn=next + done=true elseif current==base and dependent_vowel[getchar(cn)] then local cnn=getnext(cn) order_matras(cn) @@ -26559,9 +27102,14 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) end allreordered=c==stop current=getnext(c) + if done and trace_steps then + logprocess("reorder one, matra") + end end if reph or vattu then - local current,cns=start,nil + local current=start + local cns=nil + local done=false while current~=stop do local c=current local n=getnext(current) @@ -26592,6 +27140,7 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) local next=getnext(b) setlink(c,next) setlink(b,current) + done=true end elseif cns and getnext(cns)~=current then local cp=getprev(current) @@ -26599,6 +27148,7 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) setlink(cp,n) setlink(cns,current) setlink(c,cnsn) + done=true if c==stop then stop=cp break @@ -26638,11 +27188,14 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) end current=getnext(current) end + if done and trace_steps then + logprocess("reorder one, handle reph and vata") + end end if getchar(base)==c_nbsp then nbspaces=nbspaces-1 if base==stop then - stop=getprev(stop) + stop=getprev(stop) end head=remove_node(head,base) flushnode(base) @@ -26670,6 +27223,9 @@ function handlers.devanagari_reorder_matras(head,start) setlink(start,next) setlink(current,start) start=startnext + if trace_steps then + logprocess("reorder matra") + end break end else @@ -26698,6 +27254,9 @@ function handlers.devanagari_reorder_reph(head,start) local char=ischar(current,startfont) if char and getprop(current,a_syllabe)==startattr then if halant[char] then + if trace_steps then + logprocess("reorder reph, handling halant") + end local next=getnext(current) if next then local nextchar=ischar(next,startfont) @@ -26728,6 +27287,9 @@ function handlers.devanagari_reorder_reph(head,start) local char=ischar(current,startfont) if char and getprop(current,a_syllabe)==startattr then if consonant[char] and not getstate(current,s_pref) then + if trace_steps then + logprocess("reorder reph, handling consonant") + end startnext=getnext(start) head=remove_node(head,start) setlink(current,start) @@ -26752,6 +27314,9 @@ function handlers.devanagari_reorder_reph(head,start) local char=ischar(current,startfont) if char and getprop(current,a_syllabe)==startattr then if getstate(current,s_pstf) then + if trace_steps then + logprocess("reorder reph, before postscript, post base") + end startnext=getnext(start) head=remove_node(head,start) setlink(getprev(current),start) @@ -26759,12 +27324,15 @@ function handlers.devanagari_reorder_reph(head,start) start=startnext startattr=getprop(start,a_syllabe) break - elseif not c and (vowel_modifier[char] or stress_tone_mark[char] ) then + elseif not c and (vowel_modifier[char] or stress_tone_mark[char]) then c=current end current=getnext(current) else if c then + if trace_steps then + logprocess("reorder reph, before postscript") + end startnext=getnext(start) head=remove_node(head,start) setlink(getprev(c),start) @@ -26787,7 +27355,13 @@ function handlers.devanagari_reorder_reph(head,start) local state=getstate(current) if before_subscript[rephbase] and (state==s_blwf or state==s_pstf) then c=current + if trace_steps then + logprocess("reorder reph, before subscript") + end elseif after_subscript[rephbase] and (state==s_pstf) then + if trace_steps then + logprocess("reorder reph, after subscript") + end c=current end current=getnext(current) @@ -26818,6 +27392,9 @@ function handlers.devanagari_reorder_reph(head,start) end end if start~=current then + if trace_steps then + logprocess("reorder reph, to end") + end startnext=getnext(start) head=remove_node(head,start) setlink(start,getnext(current)) @@ -26827,9 +27404,8 @@ function handlers.devanagari_reorder_reph(head,start) end return head,start,true end -local reordered_pre_base_reordering_consonants={} function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) - if reordered_pre_base_reordering_consonants[start] then + if getprop(start,a_reordered) then return head,start,true end local current=start @@ -26840,6 +27416,9 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) local next=getnext(current) if char and getprop(current,a_syllabe)==startattr then if halant[char] then + if trace_steps then + logprocess("reorder pre base consonants, handle halant") + end if next then local char=ischar(next,startfont) if char and zw_char[char] and getprop(next,a_syllabe)==startattr then @@ -26851,7 +27430,7 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) head=remove_node(head,start) setlink(start,next) setlink(current,start) - reordered_pre_base_reordering_consonants[start]=true + setprop(start,"reordered",true) start=startnext return head,start,true end @@ -26865,6 +27444,9 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) while current and getprop(current,a_syllabe)==startattr do local char=ischar(current) if (not dependent_vowel[char] and (not getstate(current) or getstate(current,s_init))) then + if trace_steps then + logprocess("reorder pre base consonants, handle vowel or initial") + end startnext=getnext(start) head=remove_node(head,start) if current==head then @@ -26874,7 +27456,7 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) setlink(getprev(current),start) setlink(start,current) end - reordered_pre_base_reordering_consonants[start]=true + setprop(start,"reordered",true) start=startnext break end @@ -26906,6 +27488,9 @@ function handlers.devanagari_remove_joiners(head,start,kind,lookupname,replaceme head=stop end flushlist(start) + if trace_steps then + logprocess("remove joiners") + end return head,stop,true end local function initialize_two(font,attr) @@ -26922,15 +27507,17 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) local basepos=nil local subpos=nil local postpos=nil - reorderreph.coverage={} - rephbase[font]={} + reorderreph.coverage={} + rephbase[font]={} for i=1,#seqsubset do local subset=seqsubset[i] local kind=subset[1] local lookupcache=subset[2] if kind=="rphf" then - reorderreph.coverage[subset[3]]=true - rephbase[font][subset[3]]=subset[4] + local reph=subset[3] + local base=subset[4] + reorderreph.coverage[reph]=true + rephbase[font][reph]=base local current=start local last=getnext(stop) while current~=last do @@ -26939,15 +27526,15 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) local found=lookupcache[c] if found then local next=getnext(current) - if found[getchar(next)] or contextchain(found,next) then + if contextchain(found,next) then local afternext=next~=stop and getnext(next) if afternext and zw_char[getchar(afternext)] then current=afternext elseif current==start then setstate(current,s_rphf) - current=next + current=next else - current=next + current=next end end end @@ -26963,8 +27550,8 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) local found=lookupcache[c] if found then local next=getnext(current) - if found[getchar(next)] or contextchain(found,next) then - if (not getstate(current) and not getstate(next)) then + if contextchain(found,next) then + if not getstate(current) and not getstate(next) then setstate(current,s_pref) setstate(next,s_pref) current=next @@ -26983,10 +27570,10 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) local found=lookupcache[c] if found then local next=getnext(current) - if found[getchar(next)] or contextchain(found,next) then + if contextchain(found,next) then if next~=stop and getchar(getnext(next))==c_zwnj then current=next - elseif (not getstate(current)) then + elseif not getstate(current) then setstate(current,s_half) if not halfpos then halfpos=current @@ -27007,8 +27594,8 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) local found=lookupcache[c] if found then local next=getnext(current) - if found[getchar(next)] or contextchain(found,next) then - if (not getstate(current) and not getstate(next)) then + if contextchain(found,next) then + if not getstate(current) and not getstate(next) then setstate(current,s_blwf) setstate(next,s_blwf) current=next @@ -27028,8 +27615,8 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) local found=lookupcache[c] if found then local next=getnext(current) - if found[getchar(next)] or contextchain(found,next) then - if (not getstate(current) and not getstate(next)) then + if contextchain(found,next) then + if not getstate(current) and not getstate(next) then setstate(current,s_pstf) setstate(next,s_pstf) current=next @@ -27051,6 +27638,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) stop=getprev(stop) head=remove_node(head,current) flushnode(current) + if trace_steps then + logprocess("reorder two, remove nbsp") + end return head,stop,nbspaces else nbspaces=nbspaces+1 @@ -27091,6 +27681,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) end end end + if trace_steps then + logprocess("reorder two, handle nbsp") + end end else local last=getnext(stop) @@ -27120,7 +27713,7 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) return head,stop,nbspaces else if getstate(base) then - setstate(base,unsetvalue) + setstate(base,unsetvalue) end basepos=base end @@ -27141,14 +27734,19 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) local target=nil local cn=getnext(current) local tpm=twopart_mark[char] - while tpm do - local extra=copy_node(current) - copyinjection(extra,current) - char=tpm[1] - setchar(current,char) - setchar(extra,tpm[2]) - head=insertnodeafter(head,current,extra) - tpm=twopart_mark[char] + if tpm then + while tpm do + local extra=copy_node(current) + copyinjection(extra,current) + char=tpm[1] + setchar(current,char) + setchar(extra,tpm[2]) + head=insertnodeafter(head,current,extra) + tpm=twopart_mark[char] + end + if tpm and trace_steps then + logprocess("reorder two, handle matra") + end end if not moved[current] and dependent_vowel[char] then if pre_mark[char] then @@ -27189,6 +27787,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) end setlink(getprev(pos),current) setlink(current,pos) + if trace_steps then + logprocess("reorder two, handle pre mark") + end elseif above_mark[char] then target=basepos if subpos==basepos then @@ -27228,6 +27829,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) end setlink(current,getnext(target)) setlink(target,current) + if trace_steps then + logprocess("reorder two, handle mark") + end end end end @@ -27255,6 +27859,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) stop=prev end cn=next + if trace_steps then + logprocess("reorder two, handle halant and ra") + end end end end @@ -27287,6 +27894,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) setprev(nextnextnext,current) end setlink(nextnext,c) + if trace_steps then + logprocess("reorder two, handle nukta") + end end if stop==current then break end current=getnext(current) @@ -27298,6 +27908,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) nbspaces=nbspaces-1 head=remove_node(head,base) flushnode(base) + if trace_steps then + logprocess("reorder two, handle nbsp") + end end return head,stop,nbspaces end @@ -27385,21 +27998,42 @@ local function analyze_next_chars_one(c,font,variant) local already_below_mark local already_post_mark while dependent_vowel[v] do - local vowels=twopart_mark[v] or { v } - for k,v in next,vowels do - if pre_mark[v] and not already_pre_mark then - already_pre_mark=true - elseif above_mark[v] and not already_above_mark then - already_above_mark=true - elseif below_mark[v] and not already_below_mark then - already_below_mark=true - elseif post_mark[v] and not already_post_mark then - already_post_mark=true - else - return c - end - end - c=getnext(c) + local vowels=twopart_mark[v] + if vowels then + for k=1,#vowels do + local v=vowels[k] + if pre_mark[v] and not already_pre_mark then + already_pre_mark=true + elseif above_mark[v] and not already_above_mark then + already_above_mark=true + elseif below_mark[v] and not already_below_mark then + already_below_mark=true + elseif post_mark[v] and not already_post_mark then + already_post_mark=true + elseif devanagarihash[font].conjuncts=="continue" then + else + return c + end + end + else + if pre_mark[v] and not already_pre_mark then + already_pre_mark=true + elseif post_mark[v] and not already_post_mark then + if devanagarihash[font].conjuncts=="mixed" then + return c + else + already_post_mark=true + end + elseif below_mark[v] and not already_below_mark then + already_below_mark=true + elseif above_mark[v] and not already_above_mark then + already_above_mark=true + elseif devanagarihash[font].conjuncts=="continue" then + else + return c + end + end + c=n n=getnext(c) if not n then return c @@ -27410,7 +28044,7 @@ local function analyze_next_chars_one(c,font,variant) end end if nukta[v] then - c=getnext(c) + c=n n=getnext(c) if not n then return c @@ -27421,7 +28055,7 @@ local function analyze_next_chars_one(c,font,variant) end end if halant[v] then - c=getnext(c) + c=n n=getnext(c) if not n then return c @@ -27432,7 +28066,7 @@ local function analyze_next_chars_one(c,font,variant) end end if vowel_modifier[v] then - c=getnext(c) + c=n n=getnext(c) if not n then return c @@ -27443,7 +28077,7 @@ local function analyze_next_chars_one(c,font,variant) end end if stress_tone_mark[v] then - c=getnext(c) + c=n n=getnext(c) if not n then return c @@ -27460,11 +28094,12 @@ local function analyze_next_chars_one(c,font,variant) end end local function analyze_next_chars_two(c,font) - local n=getnext(c) + local n,v + n=getnext(c) if not n then return c end - local v=ischar(n,font) + v=ischar(n,font) if v and nukta[v] then c=n end @@ -27523,11 +28158,11 @@ local function analyze_next_chars_two(c,font) if not c then return end - local n=getnext(c) + n=getnext(c) if not n then return c end - local v=ischar(n,font) + v=ischar(n,font) if not v then return c end @@ -27567,22 +28202,43 @@ local function analyze_next_chars_two(c,font) local already_pre_mark local already_above_mark local already_below_mark - local already_post_mark - while dependent_vowel[v] do - local vowels=twopart_mark[v] or { v } - for k,v in next,vowels do - if pre_mark[v] and not already_pre_mark then - already_pre_mark=true - elseif above_mark[v] and not already_above_mark then - already_above_mark=true - elseif below_mark[v] and not already_below_mark then - already_below_mark=true - elseif post_mark[v] and not already_post_mark then - already_post_mark=true - else - return c - end - end + local already_post_mark + while dependent_vowel[v] do + local vowels=twopart_mark[v] + if vowels then + for k=1,#vowels do + local v=vowels[k] + if pre_mark[v] and not already_pre_mark then + already_pre_mark=true + elseif above_mark[v] and not already_above_mark then + already_above_mark=true + elseif below_mark[v] and not already_below_mark then + already_below_mark=true + elseif post_mark[v] and not already_post_mark then + already_post_mark=true + elseif devanagarihash[font].conjuncts=="continue" then + else + return c + end + end + else + if pre_mark[v] and not already_pre_mark then + already_pre_mark=true + elseif post_mark[v] and not already_post_mark then + if devanagarihash[font].conjuncts=="mixed" then + return c + else + already_post_mark=true + end + elseif below_mark[v] and not already_below_mark then + already_below_mark=true + elseif above_mark[v] and not already_above_mark then + already_above_mark=true + elseif devanagarihash[font].conjuncts=="continue" then + else + return c + end + end c=n n=getnext(c) if not n then @@ -27817,7 +28473,7 @@ local function method_one(head,font,attr) end else if show_syntax_errors then - local mark=mark_four[char] + local mark=mark_pre_above_below_post[char] if mark then head,current=inject_syntax_error(head,current,char) end @@ -27838,15 +28494,15 @@ local function method_one(head,font,attr) while current do local char=ischar(current,font) if char then - if n==0 and not getstate(current) then - setstate(current,s_init) - end - n=n+1 - else - n=0 - end - current=getnext(current) - end + if n==0 and not getstate(current) then + setstate(current,s_init) + end + n=n+1 + else + n=0 + end + current=getnext(current) + end return head,done end local function method_two(head,font,attr) @@ -27916,7 +28572,7 @@ local function method_two(head,font,attr) if not syllableend and show_syntax_errors then local char=ischar(current,font) if char and not getstate(current) then - local mark=mark_four[char] + local mark=mark_pre_above_below_post[char] if mark then head,current=inject_syntax_error(head,current,char) end @@ -27933,15 +28589,15 @@ local function method_two(head,font,attr) while current do local char=ischar(current,font) if char then - if n==0 and not getstate(current) then - setstate(current,s_init) - end - n=n+1 - else - n=0 - end - current=getnext(current) - end + if n==0 and not getstate(current) then + setstate(current,s_init) + end + n=n+1 + else + n=0 + end + current=getnext(current) + end return head,done end for i=1,nofscripts do @@ -27949,10 +28605,10 @@ for i=1,nofscripts do methods[scripts_two[i]]=method_two end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-osd”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-osd”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-ocl” 2a377351ae23c339764ec8ce2e8e9164] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-ocl” 2a377351ae23c339764ec8ce2e8e9164] --- if not modules then modules={} end modules ['font-ocl']={ version=1.001, @@ -28500,10 +29156,10 @@ if context then } end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-ocl”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-ocl”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-otc” 0f12230ea07b5151f75d52726977e91f] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-otc” 582295d3d1e44cd4a31e8bbc1156f7c1] --- if not modules then modules={} end modules ['font-otc']={ version=1.001, @@ -28594,6 +29250,7 @@ local function validspecification(specification,name) else dataset={ { data=specification.data } } specification.data=nil + specification.coverage=dataset specification.dataset=dataset end local first=dataset[1] @@ -28893,7 +29550,28 @@ local function addfeature(data,feature,specifications) return coverage end local prepare_single=prepare_pair - local function prepare_chain(list,featuretype,sublookups) + local function hassteps(lookups) + if lookups then + for i=1,#lookups do + local l=lookups[i] + if l then + for j=1,#l do + local l=l[j] + if l then + local n=l.nofsteps + if not n then + return true + elseif n>0 then + return true + end + end + end + end + end + end + return false + end + local function prepare_chain(list,featuretype,sublookups,nocheck) local rules=list.rules local coverage={} if rules then @@ -28929,6 +29607,7 @@ local function addfeature(data,feature,specifications) local lookups=rule.lookups or false local subtype=nil if lookups and sublookups then + local l={} for k,v in sortedhash(lookups) do local t=type(v) if t=="table" then @@ -28938,55 +29617,63 @@ local function addfeature(data,feature,specifications) v[i]={ vi } end end + l[k]=v elseif t=="number" then local lookup=sublookups[v] if lookup then - lookups[k]={ lookup } + l[k]={ lookup } if not subtype then subtype=lookup.type end elseif v==0 then - lookups[k]={ { type="gsub_remove" } } + l[k]={ { type="gsub_remove",nosteps=true } } else - lookups[k]=false + l[k]=false end else - lookups[k]=false + l[k]=false end end + if nocheck then + rule.lookups=l + end + lookups=l end if nofsequences>0 then - local hashed={} - for i=1,nofsequences do - local t={} - local s=sequence[i] - for i=1,#s do - local u=tounicode(s[i]) - if u then - t[u]=true + if hassteps(lookups) then + local hashed={} + for i=1,nofsequences do + local t={} + local s=sequence[i] + for i=1,#s do + local u=tounicode(s[i]) + if u then + t[u]=true + end + end + hashed[i]=t + end + sequence=hashed + rulesize=rulesize+1 + rulehash[rulesize]={ + nofrules, + lookuptype, + sequence, + start, + stop, + lookups, + replacements, + subtype, + } + for unic in sortedhash(sequence[start]) do + local cu=coverage[unic] + if not cu then + coverage[unic]=rulehash end end - hashed[i]=t - end - sequence=hashed - rulesize=rulesize+1 - rulehash[rulesize]={ - nofrules, - lookuptype, - sequence, - start, - stop, - lookups, - replacements, - subtype, - } - for unic in sortedhash(sequence[start]) do - local cu=coverage[unic] - if not cu then - coverage[unic]=rulehash - end + sequence.n=nofsequences + else end - sequence.n=nofsequences end end rulehash.n=rulesize @@ -29073,126 +29760,148 @@ local function addfeature(data,feature,specifications) end local askedfeatures=specification.features or everywhere local askedsteps=specification.steps or specification.subtables or { specification.data } or {} - local featuretype=normalized[specification.type or "substitution"] or "substitution" + local featuretype=specification.type or "substitution" + local featureaction=false local featureflags=specification.flags or noflags local nocheck=specification.nocheck + local mapping=specification.mapping local featureorder=specification.order or { feature } local featurechain=(featuretype=="chainsubstitution" or featuretype=="chainposition") and 1 or 0 local nofsteps=0 local steps={} local sublookups=specification.lookups local category=nil + local steptype=nil + local sequence=nil + if fonts.handlers.otf.handlers[featuretype] then + featureaction=true + else + featuretype=normalized[specification.type or "substitution"] or "substitution" + end checkflags(specification,resources) - if sublookups then - local s={} - for i=1,#sublookups do - local specification=sublookups[i] - local askedsteps=specification.steps or specification.subtables or { specification.data } or {} - local featuretype=normalized[specification.type or "substitution"] or "substitution" - local featureflags=specification.flags or noflags - local nofsteps=0 - local steps={} - for i=1,#askedsteps do - local list=askedsteps[i] - local coverage=nil - local format=nil - if featuretype=="substitution" then - coverage=prepare_substitution(list,featuretype,nocheck) - elseif featuretype=="ligature" then - coverage=prepare_ligature(list,featuretype,nocheck) - elseif featuretype=="alternate" then - coverage=prepare_alternate(list,featuretype,nocheck) - elseif featuretype=="multiple" then - coverage=prepare_multiple(list,featuretype,nocheck) - elseif featuretype=="kern" or featuretype=="move" then - format=featuretype - coverage=prepare_kern(list,featuretype) - elseif featuretype=="pair" then - format="pair" - coverage=prepare_pair(list,featuretype) - elseif featuretype=="single" then - format="single" - coverage=prepare_single(list,featuretype) - end - if coverage and next(coverage) then - nofsteps=nofsteps+1 - steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) + for k,v in next,askedfeatures do + if v[1] then + askedfeatures[k]=tohash(v) + end + end + if featureflags[1] then featureflags[1]="mark" end + if featureflags[2] then featureflags[2]="ligature" end + if featureflags[3] then featureflags[3]="base" end + if featureaction then + category="gsub" + sequence={ + features={ [feature]=askedfeatures }, + flags=featureflags, + name=feature, + order=featureorder, + type=featuretype, + nofsteps=0, + } + else + if sublookups then + local s={} + for i=1,#sublookups do + local specification=sublookups[i] + local askedsteps=specification.steps or specification.subtables or { specification.data } or {} + local featuretype=normalized[specification.type or "substitution"] or "substitution" + local featureflags=specification.flags or noflags + local nofsteps=0 + local steps={} + for i=1,#askedsteps do + local list=askedsteps[i] + local coverage=nil + local format=nil + if featuretype=="substitution" then + coverage=prepare_substitution(list,featuretype,nocheck) + elseif featuretype=="ligature" then + coverage=prepare_ligature(list,featuretype,nocheck) + elseif featuretype=="alternate" then + coverage=prepare_alternate(list,featuretype,nocheck) + elseif featuretype=="multiple" then + coverage=prepare_multiple(list,featuretype,nocheck) + elseif featuretype=="kern" or featuretype=="move" then + format=featuretype + coverage=prepare_kern(list,featuretype) + elseif featuretype=="pair" then + format="pair" + coverage=prepare_pair(list,featuretype) + elseif featuretype=="single" then + format="single" + coverage=prepare_single(list,featuretype) + end + if coverage and next(coverage) then + nofsteps=nofsteps+1 + steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) + end end + checkmerge(specification) + checksteps(specification) + s[i]={ + [stepkey]=steps, + nofsteps=nofsteps, + flags=featureflags, + type=types[featuretype], + } + end + sublookups=s + end + for i=1,#askedsteps do + local list=askedsteps[i] + local coverage=nil + local format=nil + if featuretype=="substitution" then + category="gsub" + coverage=(mapping and list) or prepare_substitution(list,featuretype,nocheck) + elseif featuretype=="ligature" then + category="gsub" + coverage=prepare_ligature(list,featuretype,nocheck) + elseif featuretype=="alternate" then + category="gsub" + coverage=prepare_alternate(list,featuretype,nocheck) + elseif featuretype=="multiple" then + category="gsub" + coverage=prepare_multiple(list,featuretype,nocheck) + elseif featuretype=="kern" or featuretype=="move" then + category="gpos" + format=featuretype + coverage=prepare_kern(list,featuretype) + elseif featuretype=="pair" then + category="gpos" + format="pair" + coverage=prepare_pair(list,featuretype) + elseif featuretype=="single" then + category="gpos" + format="single" + coverage=prepare_single(list,featuretype) + elseif featuretype=="chainsubstitution" then + category="gsub" + coverage=prepare_chain(list,featuretype,sublookups,nocheck) + elseif featuretype=="chainposition" then + category="gpos" + coverage=prepare_chain(list,featuretype,sublookups,nocheck) + else + report_otf("not registering feature %a, unknown category",feature) + return + end + if coverage and next(coverage) then + nofsteps=nofsteps+1 + steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) end - checkmerge(specification) - checksteps(specification) - s[i]={ + end + if nofsteps>0 then + sequence={ + chain=featurechain, + features={ [feature]=askedfeatures }, + flags=featureflags, + name=feature, + order=featureorder, [stepkey]=steps, nofsteps=nofsteps, - flags=featureflags, type=types[featuretype], } end - sublookups=s - end - for i=1,#askedsteps do - local list=askedsteps[i] - local coverage=nil - local format=nil - if featuretype=="substitution" then - category="gsub" - coverage=prepare_substitution(list,featuretype,nocheck) - elseif featuretype=="ligature" then - category="gsub" - coverage=prepare_ligature(list,featuretype,nocheck) - elseif featuretype=="alternate" then - category="gsub" - coverage=prepare_alternate(list,featuretype,nocheck) - elseif featuretype=="multiple" then - category="gsub" - coverage=prepare_multiple(list,featuretype,nocheck) - elseif featuretype=="kern" or featuretype=="move" then - category="gpos" - format=featuretype - coverage=prepare_kern(list,featuretype) - elseif featuretype=="pair" then - category="gpos" - format="pair" - coverage=prepare_pair(list,featuretype) - elseif featuretype=="single" then - category="gpos" - format="single" - coverage=prepare_single(list,featuretype) - elseif featuretype=="chainsubstitution" then - category="gsub" - coverage=prepare_chain(list,featuretype,sublookups) - elseif featuretype=="chainposition" then - category="gpos" - coverage=prepare_chain(list,featuretype,sublookups) - else - report_otf("not registering feature %a, unknown category",feature) - return - end - if coverage and next(coverage) then - nofsteps=nofsteps+1 - steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) - end end - if nofsteps>0 then - for k,v in next,askedfeatures do - if v[1] then - askedfeatures[k]=tohash(v) - end - end - if featureflags[1] then featureflags[1]="mark" end - if featureflags[2] then featureflags[2]="ligature" end - if featureflags[3] then featureflags[3]="base" end - local steptype=types[featuretype] - local sequence={ - chain=featurechain, - features={ [feature]=askedfeatures }, - flags=featureflags, - name=feature, - order=featureorder, - [stepkey]=steps, - nofsteps=nofsteps, - type=steptype, - } + if sequence then checkflags(sequence,resources) checkmerge(sequence) checksteps(sequence) @@ -29260,10 +29969,10 @@ end otf.enhancers.enhance=enhance otf.enhancers.register("check extra features",enhance) -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-otc”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-otc”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-onr” 6d161533f3437435e12a0ad64866df44] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-onr” 6d161533f3437435e12a0ad64866df44] --- if not modules then modules={} end modules ['font-onr']={ version=1.001, @@ -29636,10 +30345,10 @@ function readers.getinfo(filename) end end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-onr”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-onr”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-one” bd39d42055a44ca2002f8ddbbbbf27c3] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-one” bd39d42055a44ca2002f8ddbbbbf27c3] --- if not modules then modules={} end modules ['font-one']={ version=1.001, @@ -30354,10 +31063,10 @@ registerafmenhancer("normalize features",enhance_normalize_features) registerafmenhancer("check extra features",otfenhancers.enhance) registerafmenhancer("fix names",enhance_fix_names) -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-one”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-one”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-afk” 9da14e0fb22129c053acc599d1312544] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-afk” 9da14e0fb22129c053acc599d1312544] --- if not modules then modules={} end modules ['font-afk']={ version=1.001, @@ -30524,10 +31233,10 @@ fonts.handlers.afm.helpdata={ } } -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-afk”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-afk”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “fonts-tfm” f0eb5e2a8068b17ad401bb7efdba1630] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “fonts-tfm” f0eb5e2a8068b17ad401bb7efdba1630] --- if not modules then modules={} end modules ['luatex-fonts-tfm']={ version=1.001, @@ -31042,10 +31751,10 @@ registertfmfeature { } } -end --- [luaotfload, fontloader-2021-05-20.lua scope for “fonts-tfm”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “fonts-tfm”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-lua” 1fbfdf7b689b2bdfd0e3bb9bf74ce136] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-lua” 1fbfdf7b689b2bdfd0e3bb9bf74ce136] --- if not modules then modules={} end modules ['font-lua']={ version=1.001, @@ -31085,10 +31794,10 @@ function readers.lua(specification) return check_lua(specification,fullname) end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-lua”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-lua”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-def” b9d908c2338b7dc443ce9d1e9fcb7139] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-def” b9d908c2338b7dc443ce9d1e9fcb7139] --- if not modules then modules={} end modules ['font-def']={ version=1.001, @@ -31488,10 +32197,10 @@ if not context then callbacks.register('define_font',definers.read,"definition of fonts (tfmdata preparation)") end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-def”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-def”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-shp” 2bb36adb4b264a895a2c4d97e1ac1d1e] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-shp” a40862fe382d26c1f3753431254bcfdd] --- if not modules then modules={} end modules ['font-shp']={ version=1.001, @@ -31508,7 +32217,7 @@ local afm=fonts.handlers.afm local pfb=fonts.handlers.pfb local hashes=fonts.hashes local identifiers=hashes.identifiers -local version=0.010 +local version=otf.version or 0.011 local shapescache=containers.define("fonts","shapes",version,true) local streamscache=containers.define("fonts","streams",version,true) local compact_streams=false @@ -31816,7 +32525,7 @@ local function getstreamhash(fontid) local fontdata=identifiers[fontid] if fontdata then local properties=fontdata.properties - return makehash(properties.filename,properties.subfont,properties.instance) + return makehash(properties.filename,properties.subfont,properties.instance),fontdata end end local function loadstreamdata(fontdata) @@ -31856,10 +32565,10 @@ callback.register("glyph_stream_provider",function(id,index,mode) return "" end) -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-shp”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-shp”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “fonts-def” f435e0875f203f343157baeff876ec9c] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “fonts-def” f435e0875f203f343157baeff876ec9c] --- if not modules then modules={} end modules ['luatex-fonts-def']={ version=1.001, @@ -31940,10 +32649,10 @@ function fonts.definers.applypostprocessors(tfmdata) return tfmdata end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “fonts-def”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “fonts-def”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “fonts-ext” 32013cbc5d5d336be8b1d1e5879d86c4] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “fonts-ext” 32013cbc5d5d336be8b1d1e5879d86c4] --- if not modules then modules={} end modules ['luatex-fonts-ext']={ version=1.001, @@ -32129,10 +32838,10 @@ registerotffeature { } } -end --- [luaotfload, fontloader-2021-05-20.lua scope for “fonts-ext”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “fonts-ext”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-imp-tex” ceb087ef6fa2f89aed7179f60ddf8f35] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-imp-tex” ceb087ef6fa2f89aed7179f60ddf8f35] --- if not modules then modules={} end modules ['font-imp-tex']={ version=1.001, @@ -32231,10 +32940,10 @@ registerotffeature { description="arabic digits", } -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-imp-tex”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-imp-tex”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-imp-ligatures” 26ffcf089391445f7af59536c8814364] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-imp-ligatures” 26ffcf089391445f7af59536c8814364] --- if not modules then modules={} end modules ['font-imp-ligatures']={ version=1.001, @@ -32348,10 +33057,10 @@ if context then } end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-imp-ligatures”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-imp-ligatures”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-imp-italics” defcb415bc1e0a0999b27773902adc18] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-imp-italics” defcb415bc1e0a0999b27773902adc18] --- if not modules then modules={} end modules ['font-imp-italics']={ version=1.001, @@ -32455,10 +33164,10 @@ if context then registerafmfeature(dimensions_specification) end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-imp-italics”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-imp-italics”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “font-imp-effects” d05997c1826355548ec9aec1346f9f23] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “font-imp-effects” d05997c1826355548ec9aec1346f9f23] --- if not modules then modules={} end modules ['font-imp-effects']={ version=1.001, @@ -32815,10 +33524,10 @@ local specification={ registerotffeature(specification) registerafmfeature(specification) -end --- [luaotfload, fontloader-2021-05-20.lua scope for “font-imp-effects”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “font-imp-effects”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “fonts-lig” fbd251eea3810a43a8d5542319361d68] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “fonts-lig” fbd251eea3810a43a8d5542319361d68] --- fonts.handlers.otf.addfeature { @@ -34887,10 +35596,10 @@ fonts.handlers.otf.addfeature { ["type"]="ligature", } -end --- [luaotfload, fontloader-2021-05-20.lua scope for “fonts-lig”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “fonts-lig”] --- -do --- [luaotfload, fontloader-2021-05-20.lua scope for “fonts-gbn” 34e4543a02f6fbc8c2ade896cb6dc7df] --- +do --- [luaotfload, fontloader-2022-02-24.lua scope for “fonts-gbn” 34e4543a02f6fbc8c2ade896cb6dc7df] --- if not modules then modules={} end modules ['luatex-fonts-gbn']={ version=1.001, @@ -35155,7 +35864,7 @@ function nodes.simple_font_handler(head,groupcode,size,packtype,direction) return head end -end --- [luaotfload, fontloader-2021-05-20.lua scope for “fonts-gbn”] --- +end --- [luaotfload, fontloader-2022-02-24.lua scope for “fonts-gbn”] --- --- vim:ft=lua:sw=2:ts=8:et:tw=79 diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-chr.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-chr.lua index 0ac0eab14e1..3bbb693ab38 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-chr.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-chr.lua @@ -206,6 +206,7 @@ characters.classifiers={ [1562]=5, [1563]=6, [1564]=6, + [1565]=6, [1566]=6, [1567]=6, [1568]=2, @@ -672,9 +673,9 @@ characters.classifiers={ [2131]=2, [2132]=3, [2133]=2, - [2134]=4, - [2135]=4, - [2136]=4, + [2134]=3, + [2135]=3, + [2136]=3, [2137]=5, [2138]=5, [2139]=5, @@ -689,6 +690,47 @@ characters.classifiers={ [2152]=2, [2153]=3, [2154]=3, + [2160]=3, + [2161]=3, + [2162]=3, + [2163]=3, + [2164]=3, + [2165]=3, + [2166]=3, + [2167]=3, + [2168]=3, + [2169]=3, + [2170]=3, + [2171]=3, + [2172]=3, + [2173]=3, + [2174]=3, + [2175]=3, + [2176]=3, + [2177]=3, + [2178]=3, + [2179]=2, + [2180]=2, + [2181]=2, + [2182]=2, + [2183]=4, + [2184]=4, + [2185]=2, + [2186]=2, + [2187]=2, + [2188]=2, + [2189]=2, + [2190]=3, + [2192]=4, + [2193]=4, + [2200]=5, + [2201]=5, + [2202]=5, + [2203]=5, + [2204]=5, + [2205]=5, + [2206]=5, + [2207]=5, [2208]=2, [2209]=2, [2210]=2, @@ -710,6 +752,7 @@ characters.classifiers={ [2226]=3, [2227]=2, [2228]=2, + [2229]=2, [2230]=2, [2231]=2, [2232]=2, @@ -718,6 +761,26 @@ characters.classifiers={ [2235]=2, [2236]=2, [2237]=2, + [2238]=2, + [2239]=2, + [2240]=2, + [2241]=2, + [2242]=2, + [2243]=2, + [2244]=2, + [2245]=2, + [2246]=2, + [2247]=2, + [2248]=2, + [2250]=5, + [2251]=5, + [2252]=5, + [2253]=5, + [2254]=5, + [2255]=5, + [2256]=5, + [2257]=5, + [2258]=5, [2259]=5, [2260]=5, [2261]=5, @@ -837,6 +900,7 @@ characters.classifiers={ [2883]=5, [2884]=5, [2893]=5, + [2901]=5, [2902]=5, [2914]=5, [2915]=5, @@ -845,6 +909,7 @@ characters.classifiers={ [3021]=5, [3072]=5, [3076]=5, + [3132]=5, [3134]=5, [3135]=5, [3136]=5, @@ -878,6 +943,7 @@ characters.classifiers={ [3405]=5, [3426]=5, [3427]=5, + [3457]=5, [3530]=5, [3538]=5, [3539]=5, @@ -1057,6 +1123,7 @@ characters.classifiers={ [6156]=5, [6157]=5, [6158]=4, + [6159]=5, [6176]=2, [6177]=2, [6178]=2, @@ -1241,6 +1308,22 @@ characters.classifiers={ [6843]=5, [6844]=5, [6845]=5, + [6847]=5, + [6848]=5, + [6849]=5, + [6850]=5, + [6851]=5, + [6852]=5, + [6853]=5, + [6854]=5, + [6855]=5, + [6856]=5, + [6857]=5, + [6858]=5, + [6859]=5, + [6860]=5, + [6861]=5, + [6862]=5, [6912]=5, [6913]=5, [6914]=5, @@ -1373,6 +1456,7 @@ characters.classifiers={ [7671]=5, [7672]=5, [7673]=5, + [7674]=5, [7675]=5, [7676]=5, [7677]=5, @@ -1474,6 +1558,7 @@ characters.classifiers={ [43019]=5, [43045]=5, [43046]=5, + [43052]=5, [43072]=2, [43073]=2, [43074]=2, @@ -1750,6 +1835,8 @@ characters.classifiers={ [68901]=5, [68902]=5, [68903]=5, + [69291]=5, + [69292]=5, [69424]=2, [69425]=2, [69426]=2, @@ -1787,6 +1874,56 @@ characters.classifiers={ [69458]=2, [69459]=2, [69460]=3, + [69488]=2, + [69489]=2, + [69490]=2, + [69491]=2, + [69492]=3, + [69493]=3, + [69494]=2, + [69495]=2, + [69496]=2, + [69497]=2, + [69498]=2, + [69499]=2, + [69500]=2, + [69501]=2, + [69502]=2, + [69503]=2, + [69504]=2, + [69505]=2, + [69506]=5, + [69507]=5, + [69508]=5, + [69509]=5, + [69552]=2, + [69553]=4, + [69554]=2, + [69555]=2, + [69556]=3, + [69557]=3, + [69558]=3, + [69559]=4, + [69560]=2, + [69561]=3, + [69562]=3, + [69563]=2, + [69564]=2, + [69565]=3, + [69566]=2, + [69567]=2, + [69568]=4, + [69569]=2, + [69570]=3, + [69571]=3, + [69572]=2, + [69573]=4, + [69574]=4, + [69575]=4, + [69576]=4, + [69577]=3, + [69578]=2, + [69579]=1, [69633]=5, [69688]=5, [69689]=5, @@ -1803,6 +1940,9 @@ characters.classifiers={ [69700]=5, [69701]=5, [69702]=5, + [69744]=5, + [69747]=5, + [69748]=5, [69759]=5, [69760]=5, [69761]=5, @@ -1813,6 +1953,7 @@ characters.classifiers={ [69817]=5, [69818]=5, [69821]=4, + [69826]=5, [69837]=4, [69888]=5, [69889]=5, @@ -1845,6 +1986,7 @@ characters.classifiers={ [70090]=5, [70091]=5, [70092]=5, + [70095]=5, [70191]=5, [70192]=5, [70193]=5, @@ -1955,6 +2097,10 @@ characters.classifiers={ [71735]=5, [71737]=5, [71738]=5, + [71995]=5, + [71996]=5, + [71998]=5, + [72003]=5, [72148]=5, [72149]=5, [72150]=5, @@ -2092,8 +2238,78 @@ characters.classifiers={ [94096]=5, [94097]=5, [94098]=5, + [94180]=5, [113821]=5, [113822]=5, + [118528]=5, + [118529]=5, + [118530]=5, + [118531]=5, + [118532]=5, + [118533]=5, + [118534]=5, + [118535]=5, + [118536]=5, + [118537]=5, + [118538]=5, + [118539]=5, + [118540]=5, + [118541]=5, + [118542]=5, + [118543]=5, + [118544]=5, + [118545]=5, + [118546]=5, + [118547]=5, + [118548]=5, + [118549]=5, + [118550]=5, + [118551]=5, + [118552]=5, + [118553]=5, + [118554]=5, + [118555]=5, + [118556]=5, + [118557]=5, + [118558]=5, + [118559]=5, + [118560]=5, + [118561]=5, + [118562]=5, + [118563]=5, + [118564]=5, + [118565]=5, + [118566]=5, + [118567]=5, + [118568]=5, + [118569]=5, + [118570]=5, + [118571]=5, + [118572]=5, + [118573]=5, + [118576]=5, + [118577]=5, + [118578]=5, + [118579]=5, + [118580]=5, + [118581]=5, + [118582]=5, + [118583]=5, + [118584]=5, + [118585]=5, + [118586]=5, + [118587]=5, + [118588]=5, + [118589]=5, + [118590]=5, + [118591]=5, + [118592]=5, + [118593]=5, + [118594]=5, + [118595]=5, + [118596]=5, + [118597]=5, + [118598]=5, [119143]=5, [119144]=5, [119145]=5, @@ -2291,6 +2507,7 @@ characters.classifiers={ [123188]=5, [123189]=5, [123190]=5, + [123566]=5, [123628]=5, [123629]=5, [123630]=5, @@ -2459,6 +2676,7 @@ characters.indicgroups={ [2879]=true, [2902]=true, [3376]=true, + [5901]=true, }, ["after_postscript"]={ [2433]=true, @@ -2634,6 +2852,7 @@ characters.indicgroups={ [2915]=true, [3009]=true, [3010]=true, + [3132]=true, [3170]=true, [3171]=true, [3260]=true, @@ -3052,6 +3271,7 @@ characters.indicgroups={ [4225]=true, [4226]=true, [4238]=true, + [5901]=true, [43488]=true, [43489]=true, [43490]=true, @@ -3376,6 +3596,7 @@ characters.indicgroups={ [3090]=true, [3091]=true, [3092]=true, + [3165]=true, [3168]=true, [3169]=true, [3205]=true, @@ -3392,6 +3613,7 @@ characters.indicgroups={ [3218]=true, [3219]=true, [3220]=true, + [3293]=true, [3296]=true, [3297]=true, [3333]=true, @@ -3432,6 +3654,7 @@ characters.indicgroups={ [2620]=true, [2748]=true, [2876]=true, + [3132]=true, [3260]=true, }, ["post_mark"]={ @@ -3535,6 +3758,7 @@ characters.indicgroups={ [3120]=true, [3248]=true, [3376]=true, + [5901]=true, }, ["stress_tone_mark"]={ [2385]=true, @@ -3614,6 +3838,4 @@ characters.indicgroups={ }, } --- done, uncomment for testing: - --- return characters.indicgroups +-- done diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-gen.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-gen.lua index 5a6e90ceee7..1cc8537168b 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-gen.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-gen.lua @@ -189,7 +189,7 @@ local readables = { } local usingjit = jit if not caches.namespace or caches.namespace == "" or caches.namespace == "context" then - caches.namespace = 'generic' + caches.namespace = 'generic-dev' end do diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-nod.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-nod.lua index 76e36f2aabd..61587d5dedb 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-nod.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-nod.lua @@ -118,7 +118,7 @@ nuts.setcomponents = direct.setcomponents nuts.setdirection = direct.setdirection nuts.setdisc = direct.setdisc nuts.setreplace = direct.setreplace -nuts.setfield = setfield +nuts.setfield = direct.setfield nuts.setkern = direct.setkern nuts.setlink = direct.setlink nuts.setlist = direct.setlist diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-cff.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-cff.lua index ff87292baf5..4258991c674 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-cff.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-cff.lua @@ -572,6 +572,11 @@ do stack[top] = -(byte(b0)-251)*256 - byte(b1) - 108 end + -- local p_float = P("\255") * C(1) * C(1) * C(1) * C(1) / function(b0,b1,b2,b3) + -- top = top + 1 + -- stack[top] = 0 + -- end + local p_short = P("\28") * C(1) * C(1) / function(b1,b2) -- -32768 .. +32767 : b1<<8 | b2 top = top + 1 @@ -607,6 +612,7 @@ do + p_nibbles + p_single + p_double + -- + p_float + p_unsupported )^1 @@ -701,6 +707,7 @@ do local y = 0 local width = false local lsb = 0 +local result = { } local r = 0 local stems = 0 local globalbias = 0 @@ -1825,13 +1832,13 @@ do stack[top] = -t*256 + 64148 - tab[i+1] i = i + 2 else - -- a 16.16 float - local n = 0x100 * tab[i+1] + tab[i+2] - if n >= 0x8000 then - stack[top] = n - 0x10000 + (0x100 * tab[i+3] + tab[i+4])/0xFFFF - else - stack[top] = n + (0x100 * tab[i+3] + tab[i+4])/0xFFFF + -- a 16.16 float (used for italic but pretty unreliable) + local n1 = 0x100 * tab[i+1] + tab[i+2] + local n2 = 0x100 * tab[i+3] + tab[i+4] + if n1 >= 0x8000 then + n1 = n1 - 0x10000 end + stack[top] = n1 + n2/0xFFFF i = i + 5 end elseif t == 28 then @@ -1924,7 +1931,8 @@ do -- cff 1: (when cff2 strip them) elseif t == 1 or t == 3 or t == 18 or operation == 23 then p_getstem() -- at the start - if true then + if version == "cff" then +-- if true then if top > 0 then for i=1,top do r = r + 1 ; result[r] = encode[stack[i]] @@ -1939,6 +1947,7 @@ do -- cff 1: (when cff2 strip them) elseif t == 19 or t == 20 then local s = p_getmask() or 0 -- after the stems +-- if version == "cff" then if true then if top > 0 then for i=1,top do @@ -1962,7 +1971,8 @@ do i = i + 1 elseif t == 13 then hsbw() - if version == "cff" then +-- if version == "cff" then + if true then -- we do a moveto over lsb r = r + 1 ; result[r] = encode[lsb] r = r + 1 ; result[r] = chars[22] @@ -2122,7 +2132,6 @@ do end process(tab) - if hack then return x, y end @@ -2145,6 +2154,7 @@ do r = r + 1 result[r] = c_endchar local stream = concat(result) +result = nil -- if trace_charstrings then -- report("vdata: %s",stream) -- end @@ -2171,6 +2181,7 @@ do name = charset and charset[index] or nil, -- sidebearing = 0, } +result = nil else glyphs[index] = { boundingbox = boundingbox, diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua index 8d7c3d359d9..7028f7c0877 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua @@ -3806,6 +3806,7 @@ function readers.hvar(f,fontdata,specification) end local tableoffset = gotodatatable(f,fontdata,"hvar",specification.variable) if not tableoffset then + report("no hvar table, expect problems due to messy widths") return end @@ -3823,7 +3824,6 @@ function readers.hvar(f,fontdata,specification) if variationoffset > 0 then regions, deltas = readvariationdata(f,variationoffset,factors) end - if not regions then -- for now .. what to do ? return diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-osd.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-osd.lua index 2d0e685c193..34c04729039 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-osd.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-osd.lua @@ -6,7 +6,6 @@ if not modules then modules = { } end modules ['font-osd'] = { -- script devanag license = "see context related readme files" } - -- we need to check nbsphash (context only) -- A few remarks: @@ -56,6 +55,11 @@ if not modules then modules = { } end modules ['font-osd'] = { -- script devanag -- By now we have yet another incremental improved version. In the end I might -- rewrite the code. -- +-- At the start of 2022 Kauśika spent a lot of time testing combinations of fonts +-- and scripts and in the process some more tracing was added as well as a mixed +-- conjuncts options that can deal with fuzzy fonts. The machinery does what it has +-- to do but some fonts expect more magic to be applied. +-- -- Hans Hagen, PRAGMA-ADE, Hasselt NL -- Todo: @@ -81,8 +85,10 @@ if not modules then modules = { } end modules ['font-osd'] = { -- script devanag -- malayalam, oriya, tamil and tolugu but not all are checked. Also, some of the -- code below might need to be adapted to the extra scripts. -local insert, imerge, copy, tohash = table.insert, table.imerge, table.copy, table.tohash -local next, type = next, type +local insert, remove, imerge, copy, tohash = table.insert, table.remove, table.imerge, table.copy, table.tohash +local next, type, rawget = next, type, rawget +local formatters = string.formatters +local settings_to_hash = utilities.parsers.settings_to_hash local report = logs.reporter("otf","devanagari") @@ -98,6 +104,8 @@ local methods = fonts.analyzers.methods local otffeatures = fonts.constructors.features.otf local registerotffeature = otffeatures.register +local trace_steps = false + local nuts = nodes.nuts local getnext = nuts.getnext @@ -118,7 +126,7 @@ local setstate = nuts.setstate local ischar = nuts.ischar -local insertnodeafter = nuts.insertafter +local insertnodeafter = nuts.insertafter local copy_node = nuts.copy local remove_node = nuts.remove local flushlist = nuts.flushlist @@ -130,7 +138,8 @@ local unsetvalue = attributes.unsetvalue local fontdata = fonts.hashes.identifiers -local a_syllabe = attributes.private('syllabe') +local a_syllabe = "syllable" -- attributes.private('syllabe') -- can be just a property key +local a_reordered = "reordered" -- attributes.private('reordered') -- can be just a property key local dotted_circle = 0x25CC local c_nbsp = 0x00A0 @@ -156,9 +165,14 @@ replace_all_nbsp = function(head) -- delayed definition end local processcharacters = nil +local logprocess = nil if context then - local fontprocesses = fonts.hashes.processes + + local fontprocesses = fonts.hashes.processes + local tracers = nodes.tracers + local registermessage = (tracers and tracers.steppers.message) or function() end + function processcharacters(head,font) local processors = fontprocesses[font] for i=1,#processors do @@ -166,7 +180,24 @@ if context then end return head end + + -- When we'retrying to fix something it can be handy to have some more + -- details available. + + trackers.register("otf.steps", function(v) trace_steps = v end) + + logprocess = function(str) + if trace_steps then + registermessage("devanagari %s",str) + if trace_steps == "silent" then + return + end + end + report(str) + end + else + function processcharacters(head,font) local processors = fontdata[font].shared.processes for i=1,#processors do @@ -174,6 +205,11 @@ else end return head end + + logprocess = function(str) + -- do nothing + end + end -- We can assume that script are not mixed in the source but if that is the case @@ -302,7 +338,7 @@ local after_subscript = indicgroups.after_subscript local before_main = indicgroups.before_main local after_main = indicgroups.after_main -local mark_four = table.merged ( +local mark_pre_above_below_post = table.merged ( pre_mark, above_mark, below_mark, @@ -315,6 +351,14 @@ local mark_above_below_post = table.merged ( post_mark ) +-- Handy + +local devanagarihash = table.setmetatableindex(function(t,k) + local v = fontdata[k].resources.devanagari or false + t[k] = v + return v +end) + -- We use some pseudo features as we need to manipulate the nodelist based -- on information in the font as well as already applied features. We can -- probably replace some of the code below by injecting 'real' features @@ -460,7 +504,7 @@ local function initializedevanagi(tfmdata) local devanagari = resources.devanagari if not devanagari then -- - report("adding devanagari features to font") + report("adding features to font") -- local gsubfeatures = resources.features.gsub local sequences = resources.sequences @@ -491,7 +535,9 @@ local function initializedevanagi(tfmdata) for k, v in next, pre_mark do local locl = coverage[k] if locl then - if #locl > 0 then --contextchain; KE: is this right? + -- if #locl > 0 then we have a list otherwise a hash; we actually should + -- test properly for gsub_... + if #locl > 0 then for j=1,#locl do local ck = locl[j] local f = ck[4] @@ -512,6 +558,8 @@ local function initializedevanagi(tfmdata) end end end + else + -- useless next if, because locl is a table end if locl then reorder_matras.steps[1].coverage[locl] = true @@ -524,7 +572,7 @@ local function initializedevanagi(tfmdata) if basic_shaping_forms[k] then lastmatch = lastmatch + 1 if s ~= lastmatch then - table.insert(sequences, lastmatch, table.remove(sequences, s)) + insert(sequences,lastmatch,remove(sequences,s)) end end end @@ -545,9 +593,7 @@ local function initializedevanagi(tfmdata) local vatucache = { } local pstfcache = { } local seqsubset = { } - local rephstep = { - coverage = { } -- will be adapted each work - } + local rephstep = { coverage = { } } -- will be adapted each work local devanagari = { reph = false, vattu = false, @@ -577,7 +623,7 @@ local function initializedevanagi(tfmdata) local has_pstf = features.pstf if has_rphf and has_rphf[script] then devanagari.reph = true - elseif (has_blwf and has_blwf[script] ) or (has_vatu and has_vatu[script] ) then + elseif (has_blwf and has_blwf[script]) or (has_vatu and has_vatu[script]) then devanagari.vattu = true for i=1,nofsteps do local step = steps[i] @@ -585,10 +631,8 @@ local function initializedevanagi(tfmdata) if coverage then for k, v in next, coverage do for h, w in next, halant do - if v[h] then - if not blwfcache[k] then - blwfcache[k] = v - end + if v[h] and not blwfcache[k] then + blwfcache[k] = v end if has_vatu and has_vatu[script] and not vatucache[k] then vatucache[k] = v @@ -610,34 +654,40 @@ local function initializedevanagi(tfmdata) for k, v in next, ra do local r = coverage[k] if r then + -- if #r > 0 then we have a list otherwise a hash; we actually should + -- test properly for gsub_... local found = false - if #r > 0 then -- contextchain; KE: is this right? + if #r > 0 then for j=1,#r do - local ck = r[j] - local f = ck[4] + local ck = r[j] + local f = ck[4] local chainlookups = ck[6] - if chainlookups and chainlookups[f] then --KE: why is check for chainlookups[f] necessacy??? + if chainlookups then local chainlookup = chainlookups[f] - for j=1,#chainlookup do - local chainstep = chainlookup[j] - local steps = chainstep.steps - local nofsteps = chainstep.nofsteps - for i=1,nofsteps do - local step = steps[i] - local coverage = step.coverage - if coverage then - local h = coverage[k] - if h then - for k, v in next, h do - found = v and v.ligature + if chainlookup then + for j=1,#chainlookup do + local chainstep = chainlookup[j] + local steps = chainstep.steps + local nofsteps = chainstep.nofsteps + for i=1,nofsteps do + local step = steps[i] + local coverage = step.coverage + if coverage then + local h = coverage[k] + if h then + for k, v in next, h do + if v then + found = tonumber(v) or v.ligature + if found then + pre_base_reordering_consonants[found] = true + break + end + end + end if found then - pre_base_reordering_consonants[found] = true break end end - if found then - break - end end end end @@ -646,10 +696,12 @@ local function initializedevanagi(tfmdata) end else for k, v in next, r do - found = v and v.ligature - if found then - pre_base_reordering_consonants[found] = true - break + if v then + found = tonumber(v) or v.ligature + if found then + pre_base_reordering_consonants[found] = true + break + end end end end @@ -667,18 +719,21 @@ local function initializedevanagi(tfmdata) local step = steps[i] local coverage = step.coverage if coverage then - local reph, rephbase = false, false + local reph = false + local base = false if kind == "rphf" then -- rphf acts on consonant + halant for k, v in next, ra do local r = coverage[k] if r then - rephbase = k + -- if #r > 0 then we have a list otherwise a hash; we actually should + -- test properly for gsub_... + base = k local h = false - if #r > 0 then --contextchain; KE: is this right? + if #r > 0 then for j=1,#r do - local ck = r[j] - local f = ck[4] + local ck = r[j] + local f = ck[4] local chainlookups = ck[6] if chainlookups then local chainlookup = chainlookups[f] @@ -695,7 +750,7 @@ local function initializedevanagi(tfmdata) for k, v in next, halant do local h = r[k] if h then - reph = h.ligature or false + reph = tonumber(h) or h.ligature or false break end end @@ -712,7 +767,7 @@ local function initializedevanagi(tfmdata) for k, v in next, halant do local h = r[k] if h then - reph = h.ligature or false + reph = tonumber(h) or h.ligature or false break end end @@ -723,7 +778,9 @@ local function initializedevanagi(tfmdata) end end end - seqsubset[#seqsubset+1] = { kind, coverage, reph, rephbase } +-- if reph then + seqsubset[#seqsubset+1] = { kind, coverage, reph, base } +-- end end end end @@ -737,18 +794,20 @@ local function initializedevanagi(tfmdata) for k, v in next, halant do local h = coverage[k] if h then + -- if #h > 0 then we have a list otherwise a hash; we actually should + -- test properly for gsub_... local found = false - if #h > 0 then -- contextchain; KE: is this right? + if #h > 0 then for j=1,#h do - local ck = h[j] - local f = ck[4] + local ck = h[j] + local f = ck[4] local chainlookups = ck[6] if chainlookups then local chainlookup = chainlookups[f] for j=1,#chainlookup do local chainstep = chainlookup[j] - local steps = chainstep.steps - local nofsteps = chainstep.nofsteps + local steps = chainstep.steps + local nofsteps = chainstep.nofsteps for i=1,nofsteps do local step = steps[i] local coverage = step.coverage @@ -756,10 +815,12 @@ local function initializedevanagi(tfmdata) local h = coverage[k] if h then for k, v in next, h do - found = v and v.ligature - if found then - pre_base_reordering_consonants[found] = true - break + if v then + found = tonumber(v) or v.ligature + if found then + pre_base_reordering_consonants[found] = true + break + end end end if found then @@ -773,7 +834,7 @@ local function initializedevanagi(tfmdata) end else for k, v in next, h do - found = v and v.ligature + found = v and (tonumber(v) or v.ligature) if found then pre_base_reordering_consonants[found] = true break @@ -800,9 +861,9 @@ local function initializedevanagi(tfmdata) sharedfeatures["dv03"] = true -- dv03_reorder_pre_base_reordering_consonants sharedfeatures["dv04"] = true -- dv04_remove_joiners end - if script == "mlym" or script == "taml" then - devanagari.left_matra_before_base = true - end + -- if script == "mlym" or script == "taml" then + -- devanagari.movematra = "leftbeforebase" + -- end end end end @@ -816,6 +877,49 @@ registerotffeature { }, } +local function initializeconjuncts(tfmdata,value) + if value then + local resources = tfmdata.resources + local devanagari = resources.devanagari + if devanagari then + -- quit was the old situation + local conjuncts = "auto" -- mixed|continue|quit|auto + local movematra = "auto" -- default|leftbeforebase|auto + if type(value) == "string" and value ~= "auto" then + value = settings_to_hash(value) + conjuncts = rawget(value,"conjuncts") or conjuncts + movematra = rawget(value,"movematra") or movematra + end + if conjuncts == "auto" then + conjuncts = "mixed" -- for all scripts ? + end + if movematra == "auto" and + script == "mlym" or + script == "taml" then + movematra = "leftbeforebase" + else + movematra = "default" + end + devanagari.conjuncts = conjuncts + devanagari.movematra = movematra + -- + if trace_steps then + report("conjuncts %a, movematra %a",conjuncts,movematra) + end + -- + end + end +end + +registerotffeature { + name = "indic", + description = "control indic", + default = "auto", + initializers = { + node = initializeconjuncts, + }, +} + local show_syntax_errors = false local function inject_syntax_error(head,current,char) @@ -874,38 +978,44 @@ local function initialize_one(font,attr) -- we need a proper hook into the datas end -local function contextchain(contexts, n) +-- HH: somehow we can get a non context here so for now we check for .n + +local function contextchain(contexts,n) local char = getchar(n) - for k=1,#contexts do - local ck = contexts[k] - local seq = ck[3] - local f = ck[4] - local l = ck[5] - if (l - f) == 1 and seq[f+1][char] then - local ok = true - local c = n - for i=l+1,#seq do - c = getnext(c) - if not c or not seq[i][ischar(c)] then - ok = false - break - end - end - if ok then - c = getprev(n) - for i=1,f-1 do - c = getprev(c) - if not c or not seq[f-i][ischar(c)] then + if not contexts.n then + return contexts[char] + else + for k=1,#contexts do + local ck = contexts[k] + local seq = ck[3] + local f = ck[4] + local l = ck[5] + if (l - f) == 1 and seq[f+1][char] then + local ok = true + local c = n + for i=l+1,#seq do + c = getnext(c) + if not c or not seq[i][ischar(c)] then ok = false + break end end - end - if ok then - return true + if ok then + c = getprev(n) + for i=1,f-1 do + c = getprev(c) + if not c or not seq[f-i][ischar(c)] then + ok = false + end + end + end + if ok then + return true + end end end + return false end - return false end local function order_matras(c) @@ -937,11 +1047,13 @@ local function order_matras(c) end end +local swapped = table.swapped(states) + local function reorder_one(head,start,stop,font,attr,nbspaces) local reph, vattu, blwfcache, vatucache, pstfcache = initialize_one(font,attr) -- todo: a hash[font] - local devanagari = fontdata[font].resources.devanagari + -- local devanagari = devanagarihash[font] local current = start local n = getnext(start) local base = nil @@ -969,6 +1081,9 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) stop = getprev(stop) head = remove_node(head,current) flushnode(current) + if trace_steps then + logprocess("reorder one, remove nbsp") + end return head, stop, nbspaces else nbspaces = nbspaces + 1 @@ -1012,6 +1127,9 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) stop = current end end + if trace_steps then + logprocess("reorder one, handle nbsp") + end end end end @@ -1073,6 +1191,9 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) if lastcons == stop then stop = n end + if trace_steps then + logprocess("reorder one, handle halant") + end end end @@ -1102,6 +1223,9 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) if matra == stop then stop = n end + if trace_steps then + logprocess("reorder one, handle matra") + end end local current = start @@ -1148,14 +1272,19 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) end local tpm = twopart_mark[ch] - while tpm do - local extra = copy_node(n) - copyinjection(extra,n) - ch = tpm[1] - setchar(n,ch) - setchar(extra,tpm[2]) - head = insertnodeafter(head,current,extra) - tpm = twopart_mark[ch] + if tpm then + while tpm do + local extra = copy_node(n) + copyinjection(extra,n) + ch = tpm[1] + setchar(n,ch) + setchar(extra,tpm[2]) + head = insertnodeafter(head,current,extra) + tpm = twopart_mark[ch] + end + if trace_steps then + logprocess("reorder one, handle mark") + end end while c ~= stop and dependent_vowel[ch] do c = n @@ -1178,10 +1307,11 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) local bp = getprev(firstcons) local cn = getnext(current) local last = getnext(c) + local done = false while cn ~= last do -- move pre-base matras... if pre_mark[getchar(cn)] then - if devanagari.left_matra_before_base then + if devanagarihash[font].movematra == "leftbeforebase" then local prev, next = getboth(cn) setlink(prev,next) if cn == stop then @@ -1219,6 +1349,7 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) end cn = next end + done = true elseif current ~= base and dependent_vowel[getchar(cn)] then local prev, next = getboth(cn) if next then @@ -1231,6 +1362,7 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) setlink(b,cn,getnext(b)) order_matras(cn) cn = next + done = true elseif current == base and dependent_vowel[getchar(cn)] then local cnn = getnext(cn) order_matras(cn) @@ -1244,10 +1376,15 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) end allreordered = c == stop current = getnext(c) + if done and trace_steps then + logprocess("reorder one, matra") + end end if reph or vattu then - local current, cns = start, nil + local current = start + local cns = nil + local done = false while current ~= stop do local c = current local n = getnext(current) @@ -1280,6 +1417,7 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) local next = getnext(b) setlink(c,next) setlink(b,current) + done = true end elseif cns and getnext(cns) ~= current then -- todo: optimize next -- position below-base Ra (vattu) following the consonants on which it is placed (either the base consonant or one of the pre-base consonants) @@ -1288,6 +1426,7 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) setlink(cp,n) setlink(cns,current) -- cns ? setlink(c,cnsn) + done = true if c == stop then stop = cp break @@ -1327,12 +1466,15 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) end current = getnext(current) end + if done and trace_steps then + logprocess("reorder one, handle reph and vata") -- todo: boolean + end end if getchar(base) == c_nbsp then nbspaces = nbspaces - 1 if base == stop then - stop = getprev(stop) + stop = getprev(stop) end head = remove_node(head,base) flushnode(base) @@ -1373,6 +1515,9 @@ function handlers.devanagari_reorder_matras(head,start) -- no leak setlink(current,start) -- setlink(current,start,next) -- maybe start = startnext + if trace_steps then + logprocess("reorder matra") + end break -- elseif consonant[char] and (not getstate(current) or getstate(current,s_init) then -- startnext = getnext(start) @@ -1438,6 +1583,9 @@ function handlers.devanagari_reorder_reph(head,start) local char = ischar(current,startfont) if char and getprop(current,a_syllabe) == startattr then if halant[char] then + if trace_steps then + logprocess("reorder reph, handling halant") + end local next = getnext(current) if next then local nextchar = ischar(next,startfont) @@ -1475,6 +1623,9 @@ function handlers.devanagari_reorder_reph(head,start) local char = ischar(current,startfont) if char and getprop(current,a_syllabe) == startattr then if consonant[char] and not getstate(current,s_pref) then + if trace_steps then + logprocess("reorder reph, handling consonant") + end startnext = getnext(start) head = remove_node(head,start) setlink(current,start) @@ -1506,6 +1657,9 @@ function handlers.devanagari_reorder_reph(head,start) local char = ischar(current,startfont) if char and getprop(current,a_syllabe) == startattr then if getstate(current,s_pstf) then -- post-base + if trace_steps then + logprocess("reorder reph, before postscript, post base") + end startnext = getnext(start) head = remove_node(head,start) setlink(getprev(current),start) @@ -1514,12 +1668,15 @@ function handlers.devanagari_reorder_reph(head,start) start = startnext startattr = getprop(start,a_syllabe) break - elseif not c and ( vowel_modifier[char] or stress_tone_mark[char] ) then + elseif not c and (vowel_modifier[char] or stress_tone_mark[char]) then c = current end current = getnext(current) else if c then + if trace_steps then + logprocess("reorder reph, before postscript") + end startnext = getnext(start) head = remove_node(head,start) setlink(getprev(c),start) @@ -1551,7 +1708,13 @@ function handlers.devanagari_reorder_reph(head,start) local state = getstate(current) if before_subscript[rephbase] and (state == s_blwf or state == s_pstf) then c = current + if trace_steps then + logprocess("reorder reph, before subscript") + end elseif after_subscript[rephbase] and (state == s_pstf) then + if trace_steps then + logprocess("reorder reph, after subscript") + end c = current end current = getnext(current) @@ -1589,6 +1752,9 @@ function handlers.devanagari_reorder_reph(head,start) end end if start ~= current then + if trace_steps then + logprocess("reorder reph, to end") + end startnext = getnext(start) head = remove_node(head,start) setlink(start,getnext(current)) @@ -1616,10 +1782,10 @@ end -- return head, start, done -- end -local reordered_pre_base_reordering_consonants = { } -- shared ? not reset ? +-- todo: nodes -> table -> nodes function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) - if reordered_pre_base_reordering_consonants[start] then + if getprop(start,a_reordered) then return head, start, true end local current = start -- we could cache attributes here @@ -1630,6 +1796,9 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) local next = getnext(current) if char and getprop(current,a_syllabe) == startattr then if halant[char] then -- state can also be init + if trace_steps then + logprocess("reorder pre base consonants, handle halant") + end if next then local char = ischar(next,startfont) if char and zw_char[char] and getprop(next,a_syllabe) == startattr then @@ -1643,7 +1812,7 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) setlink(start,next) setlink(current,start) -- setlink(current,start,next) -- maybe - reordered_pre_base_reordering_consonants[start] = true + setprop(start,"reordered",true) start = startnext return head, start, true -- elseif consonant[char] and (not getstate(current) or getstate(current,s_init)) then @@ -1670,6 +1839,9 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) while current and getprop(current,a_syllabe) == startattr do local char = ischar(current) if (not dependent_vowel[char] and (not getstate(current) or getstate(current,s_init))) then + if trace_steps then + logprocess("reorder pre base consonants, handle vowel or initial") + end startnext = getnext(start) head = remove_node(head,start) if current == head then @@ -1679,7 +1851,7 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) setlink(getprev(current),start) setlink(start,current) end - reordered_pre_base_reordering_consonants[start] = true + setprop(start,"reordered",true) start = startnext break end @@ -1713,6 +1885,9 @@ function handlers.devanagari_remove_joiners(head,start,kind,lookupname,replaceme head = stop end flushlist(start) + if trace_steps then + logprocess("remove joiners") + end return head, stop, true end @@ -1739,8 +1914,8 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas local subpos = nil local postpos = nil - reorderreph.coverage = { } - rephbase[font] = { } + reorderreph.coverage = { } -- use local + rephbase[font] = { } -- use local for i=1,#seqsubset do @@ -1750,8 +1925,12 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas local kind = subset[1] local lookupcache = subset[2] if kind == "rphf" then - reorderreph.coverage[subset[3]] = true -- neat - rephbase[font][subset[3]] = subset[4] + -- + local reph = subset[3] + local base = subset[4] + reorderreph.coverage[reph] = true -- neat -- use local + rephbase[font][reph] = base -- use local + -- local current = start local last = getnext(stop) while current ~= last do @@ -1760,15 +1939,15 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas local found = lookupcache[c] if found then local next = getnext(current) - if found[getchar(next)] or contextchain(found, next) then --above-base: rphf Consonant + Halant + if contextchain(found, next) then -- above-base: rphf Consonant + Halant local afternext = next ~= stop and getnext(next) if afternext and zw_char[getchar(afternext)] then -- ZWJ and ZWNJ prevent creation of reph current = afternext -- getnext(next) elseif current == start then setstate(current,s_rphf) - current = next + current = next -- later again next else - current = next + current = next -- later again next end end end @@ -1784,8 +1963,8 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas local found = lookupcache[c] if found then -- pre-base: pref Halant + Consonant local next = getnext(current) - if found[getchar(next)] or contextchain(found, next) then - if (not getstate(current) and not getstate(next)) then --KE: state can also be init... + if contextchain(found, next) then + if not getstate(current) and not getstate(next) then --KE: state can also be init... setstate(current,s_pref) setstate(next,s_pref) current = next @@ -1804,10 +1983,10 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas local found = lookupcache[c] if found then local next = getnext(current) - if found[getchar(next)] or contextchain(found, next) then + if contextchain(found, next) then if next ~= stop and getchar(getnext(next)) == c_zwnj then -- zwnj prevent creation of half current = next - elseif (not getstate(current)) then --KE: state can also be init... + elseif not getstate(current) then --KE: state can also be init... setstate(current,s_half) if not halfpos then halfpos = current @@ -1828,8 +2007,8 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas local found = lookupcache[c] if found then local next = getnext(current) - if found[getchar(next)] or contextchain(found, next) then - if (not getstate(current) and not getstate(next)) then --KE: state can also be init... + if contextchain(found, next) then + if not getstate(current) and not getstate(next) then --KE: state can also be init... setstate(current,s_blwf) setstate(next,s_blwf) current = next @@ -1849,8 +2028,8 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas local found = lookupcache[c] if found then local next = getnext(current) - if found[getchar(next)] or contextchain(found, next) then - if (not getstate(current) and not getstate(next)) then -- KE: state can also be init... + if contextchain(found, next) then + if not getstate(current) and not getstate(next) then -- KE: state can also be init... setstate(current,s_pstf) setstate(next,s_pstf) current = next @@ -1877,6 +2056,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas stop = getprev(stop) head = remove_node(head,current) flushnode(current) + if trace_steps then + logprocess("reorder two, remove nbsp") + end return head, stop, nbspaces else nbspaces = nbspaces + 1 @@ -1917,6 +2099,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas end end end + if trace_steps then + logprocess("reorder two, handle nbsp") + end end else -- not Stand Alone cluster local last = getnext(stop) @@ -1948,7 +2133,7 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas return head, stop, nbspaces else if getstate(base) then -- state can also be init - setstate(base,unsetvalue) + setstate(base,unsetvalue) -- THIS RESETS THE HALF STATE end basepos = base end @@ -1973,14 +2158,19 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas local cn = getnext(current) -- not so efficient (needed for malayalam) local tpm = twopart_mark[char] - while tpm do - local extra = copy_node(current) - copyinjection(extra,current) - char = tpm[1] - setchar(current,char) - setchar(extra,tpm[2]) - head = insertnodeafter(head,current,extra) - tpm = twopart_mark[char] + if tpm then + while tpm do + local extra = copy_node(current) + copyinjection(extra,current) + char = tpm[1] + setchar(current,char) + setchar(extra,tpm[2]) + head = insertnodeafter(head,current,extra) + tpm = twopart_mark[char] + end + if tpm and trace_steps then + logprocess("reorder two, handle matra") + end end -- if not moved[current] and dependent_vowel[char] then @@ -2031,6 +2221,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas setlink(getprev(pos),current) setlink(current,pos) -- setlink(getprev(pos),current,pos) -- maybe + if trace_steps then + logprocess("reorder two, handle pre mark") + end elseif above_mark[char] then -- after main consonant target = basepos @@ -2074,13 +2267,16 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas setlink(current,getnext(target)) setlink(target,current) -- setlink(target,current,getnext(target)) -- maybe + if trace_steps then + logprocess("reorder two, handle mark") + end end end end current = cn end - -- reorder halant+Ra + -- reorder halant + Ra local current = getnext(start) local last = getnext(stop) @@ -2104,6 +2300,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas stop = prev end cn = next + if trace_steps then + logprocess("reorder two, handle halant and ra") + end end end -- after_postscript @@ -2143,6 +2342,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas setprev(nextnextnext,current) end setlink(nextnext,c) + if trace_steps then + logprocess("reorder two, handle nukta") + end end if stop == current then break end current = getnext(current) @@ -2155,6 +2357,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pas nbspaces = nbspaces - 1 head = remove_node(head, base) flushnode(base) + if trace_steps then + logprocess("reorder two, handle nbsp") + end end return head, stop, nbspaces @@ -2251,21 +2456,45 @@ local function analyze_next_chars_one(c,font,variant) -- skip one dependent vowe local already_below_mark -- = false local already_post_mark -- = false while dependent_vowel[v] do - local vowels = twopart_mark[v] or { v } - for k, v in next, vowels do - if pre_mark[v] and not already_pre_mark then - already_pre_mark = true - elseif above_mark[v] and not already_above_mark then - already_above_mark = true - elseif below_mark[v] and not already_below_mark then - already_below_mark = true - elseif post_mark[v] and not already_post_mark then - already_post_mark = true - else - return c - end - end - c = getnext(c) + local vowels = twopart_mark[v] + if vowels then + for k=1,#vowels do + local v = vowels[k] + if pre_mark[v] and not already_pre_mark then + already_pre_mark = true + elseif above_mark[v] and not already_above_mark then + already_above_mark = true + elseif below_mark[v] and not already_below_mark then + already_below_mark = true + elseif post_mark[v] and not already_post_mark then + already_post_mark = true + elseif devanagarihash[font].conjuncts == "continue" then + -- for testing + else + return c + end + end + else + if pre_mark[v] and not already_pre_mark then + already_pre_mark = true + elseif post_mark[v] and not already_post_mark then + if devanagarihash[font].conjuncts == "mixed" then + -- for messy fonts + return c + else + already_post_mark = true + end + elseif below_mark[v] and not already_below_mark then + already_below_mark = true + elseif above_mark[v] and not already_above_mark then + already_above_mark = true + elseif devanagarihash[font].conjuncts == "continue" then + -- for testing + else + return c + end + end + c = n n = getnext(c) if not n then return c @@ -2276,7 +2505,7 @@ local function analyze_next_chars_one(c,font,variant) -- skip one dependent vowe end end if nukta[v] then - c = getnext(c) + c = n n = getnext(c) if not n then return c @@ -2287,7 +2516,7 @@ local function analyze_next_chars_one(c,font,variant) -- skip one dependent vowe end end if halant[v] then - c = getnext(c) + c = n n = getnext(c) if not n then return c @@ -2298,7 +2527,7 @@ local function analyze_next_chars_one(c,font,variant) -- skip one dependent vowe end end if vowel_modifier[v] then - c = getnext(c) + c = n n = getnext(c) if not n then return c @@ -2309,7 +2538,7 @@ local function analyze_next_chars_one(c,font,variant) -- skip one dependent vowe end end if stress_tone_mark[v] then - c = getnext(c) + c = n n = getnext(c) if not n then return c @@ -2327,11 +2556,12 @@ local function analyze_next_chars_one(c,font,variant) -- skip one dependent vowe end local function analyze_next_chars_two(c,font) - local n = getnext(c) + local n, v + n = getnext(c) if not n then return c end - local v = ischar(n,font) + v = ischar(n,font) if v and nukta[v] then c = n end @@ -2393,11 +2623,11 @@ local function analyze_next_chars_two(c,font) -- This shouldn't happen I guess. return end - local n = getnext(c) + n = getnext(c) if not n then return c end - local v = ischar(n,font) + v = ischar(n,font) if not v then return c end @@ -2440,21 +2670,46 @@ local function analyze_next_chars_two(c,font) local already_above_mark -- = false local already_below_mark -- = false local already_post_mark -- = false - while dependent_vowel[v] do - local vowels = twopart_mark[v] or { v } - for k, v in next, vowels do - if pre_mark[v] and not already_pre_mark then - already_pre_mark = true - elseif above_mark[v] and not already_above_mark then - already_above_mark = true - elseif below_mark[v] and not already_below_mark then - already_below_mark = true - elseif post_mark[v] and not already_post_mark then - already_post_mark = true - else - return c - end - end + -- inefficient : too many tests but seldom more than one + while dependent_vowel[v] do + local vowels = twopart_mark[v] + if vowels then + for k=1,#vowels do + local v = vowels[k] + if pre_mark[v] and not already_pre_mark then + already_pre_mark = true + elseif above_mark[v] and not already_above_mark then + already_above_mark = true + elseif below_mark[v] and not already_below_mark then + already_below_mark = true + elseif post_mark[v] and not already_post_mark then + already_post_mark = true + elseif devanagarihash[font].conjuncts == "continue" then + -- for testing + else + return c + end + end + else + if pre_mark[v] and not already_pre_mark then + already_pre_mark = true + elseif post_mark[v] and not already_post_mark then + if devanagarihash[font].conjuncts == "mixed" then + -- for messy fonts + return c + else + already_post_mark = true + end + elseif below_mark[v] and not already_below_mark then + already_below_mark = true + elseif above_mark[v] and not already_above_mark then + already_above_mark = true + elseif devanagarihash[font].conjuncts == "continue" then + -- for testing + else + return c + end + end c = n n = getnext(c) if not n then @@ -2708,7 +2963,7 @@ local function method_one(head,font,attr) end else if show_syntax_errors then - local mark = mark_four[char] + local mark = mark_pre_above_below_post[char] if mark then head, current = inject_syntax_error(head,current,char) end @@ -2731,15 +2986,15 @@ local function method_one(head,font,attr) while current do local char = ischar(current,font) if char then - if n == 0 and not getstate(current) then - setstate(current,s_init) - end - n = n + 1 - else - n = 0 - end - current = getnext(current) - end + if n == 0 and not getstate(current) then + setstate(current,s_init) + end + n = n + 1 + else + n = 0 + end + current = getnext(current) + end return head, done end @@ -2800,7 +3055,6 @@ local function method_two(head,font,attr) syllableend = current elseif consonant[getchar(current)] then -- WHY current INSTEAD OF c ? - -- Consonant syllable: {C+[N]+<H+[<ZWNJ|ZWJ>]|<ZWNJ|ZWJ>+H>} + C+[N]+[A] + [< H+[<ZWNJ|ZWJ>] | {M}+[N]+[H]>]+[SM]+[(VD)] current = analyze_next_chars_two(current,font) -- not c ! syllableend = current @@ -2822,7 +3076,7 @@ local function method_two(head,font,attr) if not syllableend and show_syntax_errors then local char = ischar(current,font) if char and not getstate(current) then -- state can also be init - local mark = mark_four[char] + local mark = mark_pre_above_below_post[char] if mark then head, current = inject_syntax_error(head,current,char) end @@ -2841,15 +3095,19 @@ local function method_two(head,font,attr) while current do local char = ischar(current,font) if char then - if n == 0 and not getstate(current) then -- state can also be init - setstate(current,s_init) - end - n = n + 1 - else - n = 0 - end - current = getnext(current) - end + if n == 0 and not getstate(current) then -- state can also be init + setstate(current,s_init) + end + n = n + 1 + else + n = 0 + end + current = getnext(current) + end + + -- if languages.indic then + -- head = languages.indic.handler(head) + -- end return head, done end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otc.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otc.lua index 2643d022a57..2c955dbce90 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otc.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otc.lua @@ -102,8 +102,9 @@ local function validspecification(specification,name) specification = { dataset = dataset } else dataset = { { data = specification.data } } - specification.data = nil - specification.dataset = dataset + specification.data = nil + specification.coverage = dataset + specification.dataset = dataset end local first = dataset[1] if first then @@ -232,6 +233,7 @@ local function addfeature(data,feature,specifications) local unicode = tounicode(code) local description = descriptions[unicode] if not nocheck and not description then + -- todo: trace ! skip = skip + 1 else if type(replacement) == "table" then @@ -436,7 +438,30 @@ local function addfeature(data,feature,specifications) local prepare_single = prepare_pair -- we could have a better test on the spec - local function prepare_chain(list,featuretype,sublookups) + local function hassteps(lookups) + if lookups then + for i=1,#lookups do + local l = lookups[i] + if l then + for j=1,#l do + local l = l[j] + if l then + local n = l.nofsteps + if not n then + -- gsub_remove + return true + elseif n > 0 then + return true + end + end + end + end + end + end + return false + end + + local function prepare_chain(list,featuretype,sublookups,nocheck) -- todo: coveractions local rules = list.rules local coverage = { } @@ -473,6 +498,7 @@ local function addfeature(data,feature,specifications) local lookups = rule.lookups or false local subtype = nil if lookups and sublookups then + local l = { } for k, v in sortedhash(lookups) do local t = type(v) if t == "table" then @@ -483,58 +509,66 @@ local function addfeature(data,feature,specifications) v[i] = { vi } end end + l[k] = v elseif t == "number" then local lookup = sublookups[v] if lookup then - lookups[k] = { lookup } + l[k] = { lookup } if not subtype then subtype = lookup.type end elseif v == 0 then - lookups[k] = { { type = "gsub_remove" } } + l[k] = { { type = "gsub_remove", nosteps = true } } else - lookups[k] = false -- { false } -- new + l[k] = false -- { false } -- new end else - lookups[k] = false -- { false } -- new + l[k] = false -- { false } -- new end end + if nocheck then + -- fragile + rule.lookups = l --no, because checking can spoil it + end + lookups = l end if nofsequences > 0 then -- we merge coverage into one -- we copy as we can have different fonts - local hashed = { } - for i=1,nofsequences do - local t = { } - local s = sequence[i] - for i=1,#s do - local u = tounicode(s[i]) - if u then - t[u] = true + if hassteps(lookups) then + local hashed = { } + for i=1,nofsequences do + local t = { } + local s = sequence[i] + for i=1,#s do + local u = tounicode(s[i]) + if u then + t[u] = true + end end + hashed[i] = t end - hashed[i] = t - end - sequence = hashed - -- now we create the rule - rulesize = rulesize + 1 - rulehash[rulesize] = { - nofrules, -- 1 - lookuptype, -- 2 - sequence, -- 3 - start, -- 4 - stop, -- 5 - lookups, -- 6 (6/7 also signal of what to do) - replacements, -- 7 - subtype, -- 8 - } --- for unic in next, sequence[start] do - for unic in sortedhash(sequence[start]) do - local cu = coverage[unic] - if not cu then - coverage[unic] = rulehash -- can now be done cleaner i think + sequence = hashed + rulesize = rulesize + 1 + rulehash[rulesize] = { + nofrules, -- 1 + lookuptype, -- 2 + sequence, -- 3 + start, -- 4 + stop, -- 5 + lookups, -- 6 (6/7 also signal of what to do) + replacements, -- 7 + subtype, -- 8 + } + for unic in sortedhash(sequence[start]) do + local cu = coverage[unic] + if not cu then + coverage[unic] = rulehash -- can now be done cleaner i think + end end + sequence.n = nofsequences + else + -- report_otf("no steps for %a",lookuptype) -- e.g. in primes feature end - sequence.n = nofsequences end end rulehash.n = rulesize @@ -626,133 +660,164 @@ local function addfeature(data,feature,specifications) end local askedfeatures = specification.features or everywhere local askedsteps = specification.steps or specification.subtables or { specification.data } or { } - local featuretype = normalized[specification.type or "substitution"] or "substitution" + local featuretype = specification.type or "substitution" + local featureaction = false local featureflags = specification.flags or noflags local nocheck = specification.nocheck + local mapping = specification.mapping local featureorder = specification.order or { feature } local featurechain = (featuretype == "chainsubstitution" or featuretype == "chainposition") and 1 or 0 local nofsteps = 0 local steps = { } local sublookups = specification.lookups local category = nil + local steptype = nil + local sequence = nil + -- + if fonts.handlers.otf.handlers[featuretype] then + featureaction = true -- function based + else + featuretype = normalized[specification.type or "substitution"] or "substitution" + end -- checkflags(specification,resources) -- - if sublookups then - local s = { } - for i=1,#sublookups do - local specification = sublookups[i] - local askedsteps = specification.steps or specification.subtables or { specification.data } or { } - local featuretype = normalized[specification.type or "substitution"] or "substitution" - local featureflags = specification.flags or noflags - local nofsteps = 0 - local steps = { } - for i=1,#askedsteps do - local list = askedsteps[i] - local coverage = nil - local format = nil - if featuretype == "substitution" then - coverage = prepare_substitution(list,featuretype,nocheck) - elseif featuretype == "ligature" then - coverage = prepare_ligature(list,featuretype,nocheck) - elseif featuretype == "alternate" then - coverage = prepare_alternate(list,featuretype,nocheck) - elseif featuretype == "multiple" then - coverage = prepare_multiple(list,featuretype,nocheck) - elseif featuretype == "kern" or featuretype == "move" then - format = featuretype - coverage = prepare_kern(list,featuretype) - elseif featuretype == "pair" then - format = "pair" - coverage = prepare_pair(list,featuretype) - elseif featuretype == "single" then - format = "single" - coverage = prepare_single(list,featuretype) - end - if coverage and next(coverage) then - nofsteps = nofsteps + 1 - steps[nofsteps] = register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) + for k, v in next, askedfeatures do + if v[1] then + askedfeatures[k] = tohash(v) + end + end + -- + if featureflags[1] then featureflags[1] = "mark" end + if featureflags[2] then featureflags[2] = "ligature" end + if featureflags[3] then featureflags[3] = "base" end + -- + if featureaction then + + category = "gsub" + sequence = { + features = { [feature] = askedfeatures }, + flags = featureflags, + name = feature, -- redundant + order = featureorder, + type = featuretype, + -- steps = { }, + nofsteps = 0, -- just in case we test for that + } + + else + + if sublookups then + local s = { } + for i=1,#sublookups do + local specification = sublookups[i] + local askedsteps = specification.steps or specification.subtables or { specification.data } or { } + local featuretype = normalized[specification.type or "substitution"] or "substitution" + local featureflags = specification.flags or noflags + local nofsteps = 0 + local steps = { } + for i=1,#askedsteps do + local list = askedsteps[i] + local coverage = nil + local format = nil + if featuretype == "substitution" then + coverage = prepare_substitution(list,featuretype,nocheck) + elseif featuretype == "ligature" then + coverage = prepare_ligature(list,featuretype,nocheck) + elseif featuretype == "alternate" then + coverage = prepare_alternate(list,featuretype,nocheck) + elseif featuretype == "multiple" then + coverage = prepare_multiple(list,featuretype,nocheck) + elseif featuretype == "kern" or featuretype == "move" then + format = featuretype + coverage = prepare_kern(list,featuretype) + elseif featuretype == "pair" then + format = "pair" + coverage = prepare_pair(list,featuretype) + elseif featuretype == "single" then + format = "single" + coverage = prepare_single(list,featuretype) + end + if coverage and next(coverage) then + nofsteps = nofsteps + 1 + steps[nofsteps] = register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) + end end + -- + checkmerge(specification) + checksteps(specification) + -- + s[i] = { + [stepkey] = steps, + nofsteps = nofsteps, + flags = featureflags, + type = types[featuretype], + } + end + sublookups = s + end + + for i=1,#askedsteps do + local list = askedsteps[i] + local coverage = nil + local format = nil + if featuretype == "substitution" then + -- see font-imp-tweaks: we directly pass a mapping so no checks done + category = "gsub" + coverage = (mapping and list) or prepare_substitution(list,featuretype,nocheck) + elseif featuretype == "ligature" then + category = "gsub" + coverage = prepare_ligature(list,featuretype,nocheck) + elseif featuretype == "alternate" then + category = "gsub" + coverage = prepare_alternate(list,featuretype,nocheck) + elseif featuretype == "multiple" then + category = "gsub" + coverage = prepare_multiple(list,featuretype,nocheck) + elseif featuretype == "kern" or featuretype == "move" then + category = "gpos" + format = featuretype + coverage = prepare_kern(list,featuretype) + elseif featuretype == "pair" then + category = "gpos" + format = "pair" + coverage = prepare_pair(list,featuretype) + elseif featuretype == "single" then + category = "gpos" + format = "single" + coverage = prepare_single(list,featuretype) + elseif featuretype == "chainsubstitution" then + category = "gsub" + coverage = prepare_chain(list,featuretype,sublookups,nocheck) + elseif featuretype == "chainposition" then + category = "gpos" + coverage = prepare_chain(list,featuretype,sublookups,nocheck) + else + report_otf("not registering feature %a, unknown category",feature) + return end - -- - checkmerge(specification) - checksteps(specification) - -- - s[i] = { + if coverage and next(coverage) then + nofsteps = nofsteps + 1 + steps[nofsteps] = register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) + end + end + + if nofsteps > 0 then + sequence = { + chain = featurechain, + features = { [feature] = askedfeatures }, + flags = featureflags, + name = feature, -- redundant + order = featureorder, [stepkey] = steps, nofsteps = nofsteps, - flags = featureflags, type = types[featuretype], } end - sublookups = s end - for i=1,#askedsteps do - local list = askedsteps[i] - local coverage = nil - local format = nil - if featuretype == "substitution" then - category = "gsub" - coverage = prepare_substitution(list,featuretype,nocheck) - elseif featuretype == "ligature" then - category = "gsub" - coverage = prepare_ligature(list,featuretype,nocheck) - elseif featuretype == "alternate" then - category = "gsub" - coverage = prepare_alternate(list,featuretype,nocheck) - elseif featuretype == "multiple" then - category = "gsub" - coverage = prepare_multiple(list,featuretype,nocheck) - elseif featuretype == "kern" or featuretype == "move" then - category = "gpos" - format = featuretype - coverage = prepare_kern(list,featuretype) - elseif featuretype == "pair" then - category = "gpos" - format = "pair" - coverage = prepare_pair(list,featuretype) - elseif featuretype == "single" then - category = "gpos" - format = "single" - coverage = prepare_single(list,featuretype) - elseif featuretype == "chainsubstitution" then - category = "gsub" - coverage = prepare_chain(list,featuretype,sublookups) - elseif featuretype == "chainposition" then - category = "gpos" - coverage = prepare_chain(list,featuretype,sublookups) - else - report_otf("not registering feature %a, unknown category",feature) - return - end - if coverage and next(coverage) then - nofsteps = nofsteps + 1 - steps[nofsteps] = register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) - end - end - if nofsteps > 0 then + + if sequence then -- script = { lang1, lang2, lang3 } or script = { lang1 = true, ... } - for k, v in next, askedfeatures do - if v[1] then - askedfeatures[k] = tohash(v) - end - end - -- - if featureflags[1] then featureflags[1] = "mark" end - if featureflags[2] then featureflags[2] = "ligature" end - if featureflags[3] then featureflags[3] = "base" end - local steptype = types[featuretype] - local sequence = { - chain = featurechain, - features = { [feature] = askedfeatures }, - flags = featureflags, - name = feature, -- redundant - order = featureorder, - [stepkey] = steps, - nofsteps = nofsteps, - type = steptype, - } - -- checkflags(sequence,resources) checkmerge(sequence) checksteps(sequence) @@ -782,11 +847,13 @@ local function addfeature(data,feature,specifications) end end end + end end if trace_loading then report_otf("registering feature %a, affected glyphs %a, skipped glyphs %a",feature,done,skip) end + end otf.enhancers.addfeature = addfeature diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otl.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otl.lua index 4619e13dcf7..ae2dda4f396 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otl.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otl.lua @@ -26,7 +26,7 @@ if not modules then modules = { } end modules ['font-otl'] = { local lower = string.lower local type, next, tonumber, tostring, unpack = type, next, tonumber, tostring, unpack local abs = math.abs -local derivetable = table.derive +local derivetable, sortedhash = table.derive, table.sortedhash local formatters = string.formatters local setmetatableindex = table.setmetatableindex @@ -52,7 +52,7 @@ local report_otf = logs.reporter("fonts","otf loading") local fonts = fonts local otf = fonts.handlers.otf -otf.version = 3.116 -- beware: also sync font-mis.lua and in mtx-fonts +otf.version = 3.119 -- 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.pngcache = containers.define("fonts", "png", otf.version, true) @@ -218,6 +218,9 @@ function otf.load(filename,sub,instance) if cleanup == 0 then checkmemory(used,threshold,tracememory) end + if context then + otfreaders.condense(data) + end otfreaders.pack(data) report_otf("loading done") report_otf("saving %a in cache",filename) @@ -314,7 +317,7 @@ local function copytotfm(data,cache_id) local properties = derivetable(data.properties) local descriptions = derivetable(data.descriptions) local goodies = derivetable(data.goodies) - local characters = { } + local characters = { } -- newtable if we knwo how many local parameters = { } local mathparameters = { } -- @@ -504,7 +507,28 @@ local function copytotfm(data,cache_id) properties.subfont = subfont -- if not CONTEXTLMTXMODE or CONTEXTLMTXMODE == 0 then - properties.encodingbytes = 2 + -- + properties.encodingbytes = 2 +elseif CONTEXTLMTXMODE then + local duplicates = resources and resources.duplicates + if duplicates then + local maxindex = data.nofglyphs or metadata.nofglyphs + if maxindex then + for u, d in sortedhash(duplicates) do + local du = descriptions[u] + if du then + for uu in sortedhash(d) do + maxindex = maxindex + 1 + descriptions[uu].dupindex = du.index + descriptions[uu].index = maxindex + end + else + -- report_otf("no %U in font %a, duplicates ignored",u,filename) + end + end + end + end + -- end -- -- properties.name = specification.name diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oto.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oto.lua index 6f5f2fb418f..1475fcf678a 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oto.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oto.lua @@ -67,7 +67,7 @@ local function gref(descriptions,n) end local function cref(feature,sequence) - return formatters["feature %a, type %a, chain lookup %a"](feature,sequence.type,sequence.name) + return formatters["feature %a, type %a, (chain) lookup %a"](feature,sequence.type,sequence.name) end local function report_substitution(feature,sequence,descriptions,unicode,substitution) @@ -170,7 +170,7 @@ end local function makefake(tfmdata,name,present) local private = getprivate(tfmdata) local character = { intermediate = true, ligatures = { } } - resources.unicodes[name] = private + tfmdata.resources.unicodes[name] = private tfmdata.characters[private] = character tfmdata.descriptions[private] = { name = name } present[name] = private @@ -178,43 +178,55 @@ local function makefake(tfmdata,name,present) end local function make_1(present,tree,name) - for k, v in next, tree do - if k == "ligature" then - present[name] = v + if tonumber(tree) then + present[name] = v + else + for k, v in next, tree do + if k == "ligature" then + present[name] = v + else + make_1(present,v,name .. "_" .. k) + end + end + end +end + +local function make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,v) + local character = characters[preceding] + if not character then + if trace_baseinit then + report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding) + end + character = makefake(tfmdata,name,present) + end + local ligatures = character.ligatures + if ligatures then + ligatures[unicode] = { char = v } + else + character.ligatures = { [unicode] = { char = v } } + end + if done then + local d = done[name] + if not d then + done[name] = { "dummy", v } else - make_1(present,v,name .. "_" .. k) + d[#d+1] = v end end end local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done) - for k, v in next, tree do - if k == "ligature" then - local character = characters[preceding] - if not character then - if trace_baseinit then - report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding) - end - character = makefake(tfmdata,name,present) - end - local ligatures = character.ligatures - if ligatures then - ligatures[unicode] = { char = v } + if tonumber(tree) then + make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,tree) + else + for k, v in next, tree do + if k == "ligature" then + make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,v) else - character.ligatures = { [unicode] = { char = v } } + local code = present[name] or unicode + local name = name .. "_" .. k + make_2(present,tfmdata,characters,v,name,code,k,done) end - if done then - local d = done[name] - if not d then - done[name] = { "dummy", v } - else - d[#d+1] = v - end - end - else - local code = present[name] or unicode - local name = name .. "_" .. k - make_2(present,tfmdata,characters,v,name,code,k,done) end end end @@ -228,12 +240,11 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis local ligatures = { } local alternate = tonumber(value) or true and 1 local defaultalt = otf.defaultbasealternate - local trace_singles = trace_baseinit and trace_singles local trace_alternatives = trace_baseinit and trace_alternatives local trace_ligatures = trace_baseinit and trace_ligatures - -- A chain of changes is handled in font-con which is clesner because + -- A chain of changes is handled in font-con which is cleaner because -- we can have shared changes and such. if not changed then diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otr.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otr.lua index 4b154b2563d..286367ed9ea 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otr.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otr.lua @@ -682,15 +682,15 @@ local weights = { } local widths = { - [1] = "ultracondensed", - [2] = "extracondensed", - [3] = "condensed", - [4] = "semicondensed", - [5] = "normal", - [6] = "semiexpanded", - [7] = "expanded", - [8] = "extraexpanded", - [9] = "ultraexpanded", + "ultracondensed", + "extracondensed", + "condensed", + "semicondensed", + "normal", + "semiexpanded", + "expanded", + "extraexpanded", + "ultraexpanded", } setmetatableindex(weights, function(t,k) @@ -703,31 +703,31 @@ setmetatableindex(widths,function(t,k) return "normal" end) -local panoseweights = { - [ 0] = "normal", - [ 1] = "normal", - [ 2] = "verylight", - [ 3] = "light", - [ 4] = "thin", - [ 5] = "book", - [ 6] = "medium", - [ 7] = "demi", - [ 8] = "bold", - [ 9] = "heavy", - [10] = "black", +local panoseweights = { [0] = + "normal", + "normal", + "verylight", + "light", + "thin", + "book", + "medium", + "demi", + "bold", + "heavy", + "black", } -local panosewidths = { - [ 0] = "normal", - [ 1] = "normal", - [ 2] = "normal", - [ 3] = "normal", - [ 4] = "normal", - [ 5] = "expanded", - [ 6] = "condensed", - [ 7] = "veryexpanded", - [ 8] = "verycondensed", - [ 9] = "monospaced", +local panosewidths = { [0] = + "normal", + "normal", + "normal", + "normal", + "normal", + "expanded", + "condensed", + "veryexpanded", + "verycondensed", + "monospaced", } -- We implement a reader per table. @@ -1250,7 +1250,7 @@ readers.post = function(f,fontdata,specification) local version = readulong(f) fontdata.postscript = { version = version, - italicangle = round(1000*readfixed(f))/1000, + italicangle = readfixed(f), underlineposition = readfword(f), underlinethickness = readfword(f), monospaced = readulong(f), @@ -1962,9 +1962,10 @@ local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo,inst weight = weight and lower(weight), width = width and lower(width), pfmweight = metrics.weightclass or 400, -- will become weightclass - pfmwidth = metrics.widthclass or 5, -- will become widthclass + pfmwidth = metrics.widthclass or 5, -- will become widthclass panosewidth = metrics.panosewidth, panoseweight = metrics.panoseweight, + fstype = metrics.fstype or 0, -- embedding, subsetting and editing italicangle = postscript.italicangle or 0, units = fontheader.units or 0, designsize = fontdata.designsize, @@ -2522,6 +2523,10 @@ function readers.compact(fontdata) report("the %a helper is not yet implemented","compact") end +function readers.condense(fontdata) + report("the %a helper is not yet implemented","condense") +end + -- plug in local extenders = { } diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ots.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ots.lua index 30d79c4077b..7de108d4c8d 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ots.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ots.lua @@ -163,6 +163,8 @@ local trace_testruns = false registertracker("otf.testruns", function local forcediscretionaries = false local forcepairadvance = false -- for testing +local repeatablemultiples = context or false + directives.register("otf.forcediscretionaries",function(v) forcediscretionaries = v end) @@ -648,16 +650,52 @@ local function multiple_glyphs(head,start,multiple,skiphash,what,stop) -- what t insertnodeafter(head,start,n) start = n end - if what == true then - -- we're ok - elseif what > 1 then - local m = multiple[nofmultiples] - for i=2,what do - local n = copy_node(start) -- ignore components - resetinjection(n) - setchar(n,m) - insertnodeafter(head,start,n) - start = n + if what ~= true and repeatablemultiples then + -- This is just some experimental code; we might introduce gsub_extensible + -- some day instead. Beware: when we have a feature that mixes alternates and + -- multiples we need to make sure we don't handle the alternate string values + -- here. This might eventually become an lmtx only feature. + local kind = type(what) + local m, f, l + if kind == "string" then + local what, n = string.match(what,"^repeat(.-)[:=](%d+)$") + if what == "middle" then + m = tonumber(n) + elseif what == "first" then + f = tonumber(n) + elseif what == "last" then + l = tonumber(n) + end + elseif kind == "table" then + -- won't happen because currently we don't split these values + m = what.middle + f = what.first + l = what.last + end + if f or m or l then + if m and m > 1 and nofmultiples == 3 then + local middle = getnext(first) + for i=2,m do + local n = copynode(middle) -- ignore components + resetinjection(n) + insertnodeafter(head,first,n) + end + end + if f and f > 1 then + for i=2,f do + local n = copynode(first) -- ignore components + resetinjection(n) + insertnodeafter(head,first,n) + end + end + if l and l > 1 then + for i=2,l do + local n = copynode(start) -- ignore components + resetinjection(n) + insertnodeafter(head,start,n) + start = n + end + end end end end @@ -763,7 +801,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip while current do local char = ischar(current,currentfont) if char then - local lg = ligature[char] + local lg = not tonumber(ligature) and ligature[char] if lg then stop = current ligature = lg @@ -776,14 +814,14 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip end end if stop then - local lig = ligature.ligature - if lig then + local ligature = tonumber(ligature) or ligature.ligature + if ligature then if trace_ligatures then local stopchar = getchar(stop) - head, start = markstoligature(head,start,stop,lig) + head, start = markstoligature(head,start,stop,ligature) logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(getchar(start))) else - head, start = markstoligature(head,start,stop,lig) + head, start = markstoligature(head,start,stop,ligature) end return head, start, true, false else @@ -799,7 +837,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip if skiphash and skiphash[char] then current = getnext(current) else - local lg = ligature[char] + local lg = not tonumber(ligature) and ligature[char] if lg then if marks[char] then hasmarks = true @@ -833,20 +871,20 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip local match if replace then local char = ischar(replace,currentfont) - if char and ligature[char] then + if char and (not tonumber(ligature) and ligature[char]) then match = true end end if not match and pre then local char = ischar(pre,currentfont) - if char and ligature[char] then + if char and (not tonumber(ligature) and ligature[char]) then match = true end end if not match and not pre or not replace then local n = getnext(discfound) local char = ischar(n,currentfont) - if char and ligature[char] then + if char and (not tonumber(ligature) and ligature[char]) then match = true end end @@ -890,24 +928,26 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip return head, start, true, true end end - local lig = ligature.ligature - if lig then + local ligature = tonumber(ligature) or ligature.ligature + if ligature then if stop then if trace_ligatures then local stopchar = getchar(stop) - -- head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks) - head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks) - logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(lig)) + -- head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,discfound,hasmarks) + head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks) + logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(ligature)) + -- we can have a rare case of multiple disc in a lig but that makes no sense language wise but if really + -- needed we could backtrack if we're in a disc node else - -- head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks) - head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks) + -- head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,discfound,hasmarks) + head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks) end else -- weird but happens (in some arabic font) resetinjection(start) - setchar(start,lig) + setchar(start,ligature) if trace_ligatures then - logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(lig)) + logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(ligature)) end end return head, start, true, false @@ -1050,7 +1090,7 @@ function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode, end return head, start, true elseif trace_bugs then - -- onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + -- onetimemessage(currentfont,basechar,"no base anchors") logwarning("%s: mark %s is not anchored to %s",pref(dataset,sequence),gref(markchar),gref(basechar)) end elseif trace_bugs then @@ -1116,7 +1156,7 @@ function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlm end elseif trace_bugs then -- logwarning("%s: char %s is missing in font",pref(dataset,sequence),gref(basechar)) - onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + onetimemessage(currentfont,basechar,"no base anchors") end elseif trace_bugs then logwarning("%s: prev node is no char, case %i",pref(dataset,sequence),1) @@ -1484,7 +1524,7 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup current = getnext(current) -- end else - local lg = ligatures[schar] + local lg = not tonumber(ligatures) and ligatures[schar] if lg then ligatures = lg last = current @@ -1503,7 +1543,7 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup end end end - local ligature = ligatures.ligature + local ligature = tonumber(ligatures) or ligatures.ligature if ligature then if chainindex then stop = last @@ -1856,7 +1896,7 @@ function chainprocs.gpos_cursive(head,start,stop,dataset,sequence,currentlookup, end end elseif trace_bugs then - onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) + onetimemessage(currentfont,startchar,"no entry anchors") end break end @@ -1978,7 +2018,9 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) local chainproc = chainprocs[chainkind] if chainproc then local ok - head, start, ok = chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash) + -- HH: chainindex 1 added here (for KAI to check too), there are weird ligatures e.g. + -- char + mark -> char where mark has to disappear + head, start, ok = chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,1) if ok then done = true end @@ -2471,6 +2513,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s local ck = contexts[k] local seq = ck[3] local f = ck[4] -- first current +local last = start if not startchar or not seq[f][startchar] then -- report("no hit in %a at %i of %i contexts",sequence.type,k,nofcontexts) goto next @@ -2481,7 +2524,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s else local l = ck[5] -- last current local current = start - local last = start +-- local last = start -- current match @@ -3409,7 +3452,7 @@ local function t_run_single(start,stop,font,attr,lookupcache) while s do local char = ischar(s,font) if char then - local lg = lookupmatch[char] + local lg = not tonumber(lookupmatch) and lookupmatch[char] if lg then if sstop then d = 1 @@ -3439,7 +3482,7 @@ local function t_run_single(start,stop,font,attr,lookupcache) break end end - if l and l.ligature then -- so we test for ligature + if l and (tonumber(l) or l.ligature) then -- so we test for ligature lastd = d end -- why not: if not l then break elseif l.ligature then return d end @@ -3580,7 +3623,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps) while s do local char = ischar(s) if char then - local lg = lookupmatch[char] + local lg = not tonumber(lookupmatch) and lookupmatch[char] if lg then if sstop then d = 1 @@ -3610,7 +3653,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps) break end end - if l and l.ligature then + if l and (tonumber(l) or l.ligature) then lastd = d end end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oup.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oup.lua index c93ad474386..3bd2fe48288 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oup.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oup.lua @@ -37,13 +37,16 @@ local f_index = formatters["I%05X"] local f_character_y = formatters["%C"] local f_character_n = formatters["[ %C ]"] -local check_duplicates = true -- can become an option (pseudo feature) / aways needed anyway -local check_soft_hyphen = true -- can become an option (pseudo feature) / needed for tagging +local check_duplicates = true -- can become an option (pseudo feature) / always needed anyway +local check_soft_hyphen = context -- only in context directives.register("otf.checksofthyphen",function(v) - check_soft_hyphen = v + check_soft_hyphen = v -- only for testing end) +-- After (!) the unicodes have been resolved we compact ligature tables so before that happens +-- we don't need to check for numbers. + local function replaced(list,index,replacement) if type(list) == "number" then return replacement @@ -453,31 +456,48 @@ local function copyduplicates(fontdata) local resources = fontdata.resources local duplicates = resources.duplicates if check_soft_hyphen then - -- ebgaramond has a zero width empty soft hyphen - -- antykwatorunsks lacks a soft hyphen - local ds = descriptions[0xAD] - if not ds or ds.width == 0 then - if ds then + -- ebgaramond has a zero width empty soft hyphen + -- antykwatorunska lacks a soft hyphen + -- lucidaot has a halfwidth soft hyphen + + -- local dh = descriptions[0x2D] + -- if dh then + -- descriptions[0xAD] = nil + -- local d = duplicates[0x2D] + -- if d then + -- d[#d+1] = { [0xAD] = true } + -- else + -- duplicates[0x2D] = { [0xAD] = true } + -- end + -- end + + local dh = descriptions[0x2D] + if dh then + local ds = descriptions[0xAD] + if not ds or ds.width ~= dh.width then descriptions[0xAD] = nil - if trace_unicodes then - report_unicodes("patching soft hyphen") + if ds then + if trace_unicodes then + report_unicodes("patching soft hyphen") + end + else + if trace_unicodes then + report_unicodes("adding soft hyphen") + end end - else - if trace_unicodes then - report_unicodes("adding soft hyphen") + if not duplicates then + duplicates = { } + resources.duplicates = duplicates + end + local d = duplicates[0x2D] + if d then + d[0xAD] = true + else + duplicates[0x2D] = { [0xAD] = true } end - end - if not duplicates then - duplicates = { } - resources.duplicates = duplicates - end - local dh = duplicates[0x2D] - if dh then - dh[#dh+1] = { [0xAD] = true } - else - duplicates[0x2D] = { [0xAD] = true } end end + end if duplicates then for u, d in next, duplicates do @@ -759,6 +779,7 @@ local function unifyglyphs(fontdata,usenames) local resources = fontdata.resources local zero = glyphs[0] local zerocode = zero.unicode + local nofglyphs = #glyphs if not zerocode then zerocode = private zero.unicode = zerocode @@ -775,7 +796,7 @@ local function unifyglyphs(fontdata,usenames) -- if names then -- seldom uses, we don't issue message ... this branch might even go away - for index=1,#glyphs do + for index=1,nofglyphs do local glyph = glyphs[index] local unicode = glyph.unicode -- this is the primary one if not unicode then @@ -808,7 +829,7 @@ local function unifyglyphs(fontdata,usenames) descriptions[unicode] = glyph end elseif trace_unicodes then - for index=1,#glyphs do + for index=1,nofglyphs do local glyph = glyphs[index] local unicode = glyph.unicode -- this is the primary one if not unicode then @@ -849,7 +870,7 @@ local function unifyglyphs(fontdata,usenames) descriptions[unicode] = glyph end else - for index=1,#glyphs do + for index=1,nofglyphs do local glyph = glyphs[index] local unicode = glyph.unicode -- this is the primary one if not unicode then @@ -876,8 +897,8 @@ local function unifyglyphs(fontdata,usenames) end end -- - for index=1,#glyphs do - local math = glyphs[index].math + for index=1,nofglyphs do + local math = glyphs[index].math if math then local list = math.vparts if list then @@ -902,7 +923,7 @@ local function unifyglyphs(fontdata,usenames) -- local colorpalettes = resources.colorpalettes if colorpalettes then - for index=1,#glyphs do + for index=1,nofglyphs do local colors = glyphs[index].colors if colors then for i=1,#colors do @@ -918,6 +939,7 @@ local function unifyglyphs(fontdata,usenames) fontdata.names = names fontdata.descriptions = descriptions fontdata.hashmethod = hashmethod + fontdata.nofglyphs = nofglyphs -- return indices, names end @@ -1024,6 +1046,10 @@ function readers.getcomponents(fontdata) -- handy for resolving ligatures when n local function traverse(p,k,v) if k == "ligature" then collected[v] = { unpack(l) } + elseif tonumber(v) then + insert(l,k) + collected[v] = { unpack(l) } + remove(l) else insert(l,k) for k, vv in next, v do @@ -1211,7 +1237,6 @@ local function tabstr_flat(t) end local function tabstr_mixed(t) -- indexed - local s = { } local n = #t if n == 0 then return "" @@ -1225,6 +1250,7 @@ local function tabstr_mixed(t) -- indexed return tostring(k) -- number or string end else + local s = { } for i=1,n do local k = t[i] if k == true then @@ -1334,6 +1360,21 @@ function readers.pack(data) end end + -- local function pack_indexed(v) -- less code + -- local tag = concat(v," ") + -- local ht = h[tag] + -- if ht then + -- c[ht] = c[ht] + 1 + -- else + -- ht = nt + 1 + -- t[ht] = v + -- c[ht] = 1 + -- h[tag] = ht + -- nt = ht + -- end + -- return ht + -- end + local function pack_mixed(v) local tag = tabstr_mixed(v) local ht = h[tag] @@ -2782,6 +2823,91 @@ function readers.compact(data) end end +if CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 then + + local done = 0 + + local function condense_1(k,v,t) + if type(v) == "table" then + local u = false + local l = false + for k, v in next, v do + if k == "ligature" then + l = v + if u then + break + end + elseif u then + break + else + u = true + end + end + if l and not u then + t[k] = l + done = done + 1 + end + if u then + for k, vv in next, v do + if k ~= "ligature" then + condense_1(k,vv,v) + end + end + end + end + end + + local function condensesteps_1(lookup) + done = 0 + if lookup.type == "gsub_ligature" then + local steps = lookup.steps + if steps then + for i=1,#steps do + local step = steps[i] + local coverage = step.coverage + if coverage then + for k, v in next, coverage do + if condense_1(k,v,coverage) then + coverage[k] = v.ligature + done = done + 1 + end + end + end + end + end + end + return done + end + + function readers.condense(data) + if not data or data.condensed then + return + else + data.condensed = true + end + local resources = data.resources + local condensed = 0 + local function condense(what) + local lookups = resources[what] + if lookups then + for i=1,#lookups do + condensed = condensed + condensesteps_1(lookups[i]) + end + elseif trace_optimizations then + report_optimizations("no lookups in %a",what) + end + end + condense("sequences") + condense("sublookups") + if trace_optimizations then + if condensed > 0 then + report_optimizations("%i ligatures condensed",condensed) + end + end + end + +end + local function mergesteps(t,k) if k == "merged" then local merged = { } @@ -2901,7 +3027,7 @@ function readers.expand(data) -- about 15% on arabtype .. then moving the a test also saves a bit (even when -- often a is not set at all so that one is a bit debatable - local function expandlookups(sequences) + local function expandlookups(sequences,whatever) if sequences then -- we also need to do sublookups for i=1,#sequences do @@ -2977,6 +3103,9 @@ function readers.expand(data) if lookups then for k, v in next, lookups do -- actually this one is indexed local lookup = sublookups[v] +if not lookup and whatever then + lookup = whatever[v] +end if lookup then lookups[k] = lookup if not subtype then @@ -3057,5 +3186,5 @@ function readers.expand(data) end expandlookups(sequences) - expandlookups(sublookups) + expandlookups(sublookups,sequences) end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-shp.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-shp.lua index ecf0c941802..2ca3011a5bd 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-shp.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-shp.lua @@ -17,7 +17,7 @@ local pfb = fonts.handlers.pfb local hashes = fonts.hashes local identifiers = hashes.identifiers -local version = 0.010 +local version = otf.version or 0.011 local shapescache = containers.define("fonts", "shapes", version, true) local streamscache = containers.define("fonts", "streams", version, true) @@ -346,7 +346,7 @@ local function getstreamhash(fontid) local fontdata = identifiers[fontid] if fontdata then local properties = fontdata.properties - return makehash(properties.filename,properties.subfont,properties.instance) + return makehash(properties.filename,properties.subfont,properties.instance), fontdata end end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ttf.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ttf.lua index a2732aa5f2e..a2658d595ab 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ttf.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ttf.lua @@ -35,7 +35,7 @@ if not modules then modules = { } end modules ['font-ttf'] = { local next, type, unpack = next, type, unpack local band, rshift = bit32.band, bit32.rshift -local sqrt, round = math.sqrt, math.round +local sqrt, round, abs, min, max = math.sqrt, math.round, math.abs, math.min, math.max local char, rep = string.char, string.rep local concat = table.concat local idiv = number.idiv @@ -136,8 +136,12 @@ local function mergecomposites(glyphs,shapes) local y = p[2] nofpoints = nofpoints + 1 points[nofpoints] = { + -- unifractur : u n + -- seguiemj : 0x270E 0x2710 xscale * x + xrotate * y + xoffset, yscale * y + yrotate * x + yoffset, +-- xscale * x + yrotate * y + xoffset, +-- xrotate * x + yscale * y + yoffset, p[3] } end @@ -203,77 +207,191 @@ end -- We had two loops (going backward) but can do it in one loop .. but maybe we -- should only accept fonts with proper hvar tables. +-- dowidth is kind of hack ... fonts are not always ok wrt these extra points + +local xv = { } -- we share this cache +local yv = { } -- we share this cache + local function applyaxis(glyph,shape,deltas,dowidth) local points = shape.points if points then local nofpoints = #points - local h = nofpoints + 2 -- weird, the example font seems to have left first - local l = nofpoints + 1 - ----- v = nofpoints + 3 - ----- t = nofpoints + 4 local dw = 0 local dl = 0 for i=1,#deltas do local deltaset = deltas[i] local xvalues = deltaset.xvalues local yvalues = deltaset.yvalues - local dpoints = deltaset.points - local factor = deltaset.factor - if dpoints then - -- todo: interpolate - local nofdpoints = #dpoints - for i=1,nofdpoints do - local d = dpoints[i] - local p = points[d] - if p then - if xvalues then - local x = xvalues[i] - if x and x ~= 0 then - p[1] = p[1] + factor * x + if xvalues and yvalues then + local dpoints = deltaset.points + local factor = deltaset.factor + if dpoints then + local cnt = #dpoints + if dowidth then + cnt = cnt - 4 + end + if cnt == 1 then + local d = dpoints[1] + local x = xvalues[d] * factor + local y = yvalues[d] * factor + for i=1,nofpoints do + local p = points[i] + if x ~= 0 then + p[1] = p[1] + x + end + if y ~= 0 then + p[2] = p[2] + y end end - if yvalues then - local y = yvalues[i] - if y and y ~= 0 then - p[2] = p[2] + factor * y + elseif cnt > 0 then + -- Not the most efficient solution but we seldom do this. We + -- actually need to avoid the extra points here but I'll deal + -- with that when needed. + local function find(i) + local prv = cnt + for j=1,cnt do + local nxt = dpoints[j] + if nxt == i then + return false, j, false + elseif nxt > i then + return prv, false, j + end + prv = j + end + return prv, false, 1 + end + -- We need the first and last points untouched so we first + -- collect data. + for i=1,nofpoints do + local d1, d2, d3 = find(i) + local p2 = points[i] + if d2 then + xv[i] = xvalues[d2] + yv[i] = yvalues[d2] + else + local n1 = dpoints[d1] + local n3 = dpoints[d3] + -- Some day I need to figure out these extra points but + -- I'll wait till the standard is more clear and fonts + -- become better (ntg-context: fraunces.ttf > abcdef). + if n1 > nofpoints then + n1 = nofpoints + end + if n3 > nofpoints then + n3 = nofpoints + end + -- + local p1 = points[n1] + local p3 = points[n3] + local p1x = p1[1] + local p2x = p2[1] + local p3x = p3[1] + local p1y = p1[2] + local p2y = p2[2] + local p3y = p3[2] + local x1 = xvalues[d1] + local y1 = yvalues[d1] + local x3 = xvalues[d3] + local y3 = yvalues[d3] + -- + local fx + local fy + -- + if p1x == p3x then + if x1 == x3 then + fx = x1 + else + fx = 0 + end + elseif p2x <= min(p1x,p3x) then + if p1x < p3x then + fx = x1 + else + fx = x3 + end + elseif p2x >= max(p1x,p3x) then + if p1x > p3x then + fx = x1 + else + fx = x3 + end + else + fx = (p2x - p1x)/(p3x - p1x) + fx = (1 - fx) * x1 + fx * x3 + end + -- + if p1y == p3y then + if y1 == y3 then + fy = y1 + else + fy = 0 + end + elseif p2y <= min(p1y,p3y) then + if p1y < p3y then + fy = y1 + else + fy = y3 + end + elseif p2y >= max(p1y,p3y) then + if p1y > p3y then + fy = y1 + else + fy = y3 + end + else + fy = (p2y - p1y)/(p3y - p1y) + fy = (1 - fy) * y1 + fy * y3 + end + -- -- maybe: + -- if p1y ~= p3y then + -- fy = (p2y - p1y)/(p3y - p1y) + -- fy = (1 - fy) * y1 + fy * y3 + -- elseif abs(p1y-p2y) < abs(p3y-p2y) then + -- fy = y1 + -- else + -- fy = y3 + -- end + -- + xv[i] = fx + yv[i] = fy end end - elseif dowidth then - -- we've now ran into phantom points which is a bit fuzzy because: - -- are there gaps in there? - -- - -- todo: move this outside the loop (when we can be sure of all 4 being there) - if d == h then - -- we have a phantom point hadvance - local x = xvalues[i] - if x then - dw = dw + factor * x + for i=1,nofpoints do + local pi = points[i] + local fx = xv[i] + local fy = yv[i] + if fx ~= 0 then + pi[1] = pi[1] + factor * fx end - elseif d == l then - local x = xvalues[i] - if x then - dl = dl + factor * x + if fy ~= 0 then + pi[2] = pi[2] + factor * fy end end + else + report("bad deltapoint data, maybe a missing hvar table") end - end - else - for i=1,nofpoints do - local p = points[i] - if xvalues then + else + for i=1,nofpoints do + local p = points[i] local x = xvalues[i] - if x and x ~= 0 then - p[1] = p[1] + factor * x - end - end - if yvalues then - local y = yvalues[i] - if y and y ~= 0 then - p[2] = p[2] + factor * y + if x then + local y = yvalues[i] + if x ~= 0 then + p[1] = p[1] + factor * x + end + if y ~= 0 then + p[2] = p[2] + factor * y + end + else + break end end end if dowidth then + local h = nofpoints + 2 -- weird, the example font seems to have left first + local l = nofpoints + 1 + ----- v = nofpoints + 3 + ----- t = nofpoints + 4 local x = xvalues[h] if x then dw = dw + factor * x @@ -930,13 +1048,13 @@ local function readcomposite(f) yoffset = yoffset * yscale end elseif band(flags,0x0080) ~= 0 then -- f_matrix - xscale = read2dot14(f) - xrotate = read2dot14(f) - yrotate = read2dot14(f) - yscale = read2dot14(f) + xscale = read2dot14(f) -- xxpart + xrotate = read2dot14(f) -- yxpart + yrotate = read2dot14(f) -- xypart + yscale = read2dot14(f) -- yypart if f_xyarg and f_offset then - xoffset = xoffset * sqrt(xscale ^2 + xrotate^2) - yoffset = yoffset * sqrt(yrotate^2 + yscale ^2) + xoffset = xoffset * sqrt(xscale ^2 + yrotate^2) -- was xrotate + yoffset = yoffset * sqrt(xrotate^2 + yscale ^2) -- was yrotate end end nofcomponents = nofcomponents + 1 @@ -1119,50 +1237,6 @@ end local function readdeltas(f,nofpoints) local deltas = { } local p = 0 - local z = 0 - while nofpoints > 0 do - local control = readbyte(f) -if not control then - break -end - local allzero = band(control,0x80) ~= 0 - local runlength = band(control,0x3F) + 1 - if allzero then - z = z + runlength - else - local runreader = band(control,0x40) ~= 0 and readshort or readinteger - if z > 0 then - for i=1,z do - p = p + 1 - deltas[p] = 0 - end - z = 0 - end - for i=1,runlength do - p = p + 1 - deltas[p] = runreader(f) - end - end - nofpoints = nofpoints - runlength - end - -- saves space --- if z > 0 then --- for i=1,z do --- p = p + 1 --- deltas[p] = 0 --- end --- end - if p > 0 then - -- forget about trailing zeros - return deltas - else - -- forget about all zeros - end -end - -local function readdeltas(f,nofpoints) - local deltas = { } - local p = 0 while nofpoints > 0 do local control = readbyte(f) if control then @@ -1330,7 +1404,8 @@ function readers.gvar(f,fontdata,specification,glyphdata,shapedata) -- local start = start and start[i] or 0 -- local stop = stop and stop [i] or 0 local start = start and start[i] or (peak < 0 and peak or 0) - local stop = stop and stop [i] or (peak > 0 and peak or 0) + local stop = stop and stop [i] or (peak > 0 and peak or 0) -- or 1 ? +-- local stop = stop and stop [i] or (peak > 0 and peak or 1) -- or 1 ? -- do we really need these tests ... can't we assume sane values if start > peak or peak > stop then -- * 1 @@ -1343,7 +1418,6 @@ function readers.gvar(f,fontdata,specification,glyphdata,shapedata) s = 0 break elseif f < peak then --- s = - s * (f - start) / (peak - start) s = s * (f - start) / (peak - start) elseif f > peak then s = s * (stop - f) / (stop - peak) diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-enc.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-enc.lua index 2bc6b71bf49..d8725c21409 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-enc.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-enc.lua @@ -16,10 +16,18 @@ fonts.encodings = encodings encodings.agl = { } encodings.known = { } +encodings.glyphlistfilename = "font-age.lua" + setmetatable(encodings.agl, { __index = function(t,k) if k == "unicodes" then logs.report("fonts","loading (extended) adobe glyph list") - local unicodes = dofile(resolvers.findfile("font-age.lua")) + local foundname = resolvers.findfile(encodings.glyphlistfilename) or "" + local unicodes = foundname ~= "" and dofile(foundname) + if type(unicodes) ~= "table" then + logs.report("fonts","missing or invalid (extended) adobe glyph list") + -- no message + unicodes = { } + end encodings.agl = { unicodes = unicodes } return unicodes else diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-file.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-file.lua index d0af94f09d4..9f8fd654833 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-file.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-file.lua @@ -456,7 +456,7 @@ function file.join(one, two, three, ...) if not two then return one == "" and one or lpegmatch(reslasher,one) end - if one == "" then + if not one or one == "" then return lpegmatch(stripper,three and concat({ two, three, ... },"/") or two) end if lpegmatch(isnetwork,one) then diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-io.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-io.lua index a955262a31e..6bf7a97bda2 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-io.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-io.lua @@ -147,9 +147,12 @@ function io.copydata(source,target,action) end end -function io.savedata(filename,data,joiner) - local f = open(filename,"wb") +function io.savedata(filename,data,joiner,append) + local f = open(filename,append and "ab" or "wb") if f then + if append and joiner and f:seek("end") > 0 then + f:write(joiner) + end if type(data) == "table" then f:write(concat(data,joiner or "")) elseif type(data) == "function" then @@ -288,7 +291,8 @@ end io.noflines = noflines --- inlined is faster ... beware, better use util-fil +-- inlined is faster ... beware, better use util-fil so these are obsolete +-- and will go local nextchar = { [ 4] = function(f) diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-lpeg.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-lpeg.lua index 50306e4ab69..5f3bea08ca6 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-lpeg.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-lpeg.lua @@ -665,12 +665,12 @@ end -- lpeg.print(lpeg.P("a","b","c")) -- lpeg.print(lpeg.S("a","b","c")) --- print(lpeg.count("äáàa",lpeg.P("á") + lpeg.P("à"))) --- print(lpeg.count("äáàa",lpeg.UP("áà"))) --- print(lpeg.count("äáàa",lpeg.US("àá"))) --- print(lpeg.count("äáàa",lpeg.UR("aá"))) --- print(lpeg.count("äáàa",lpeg.UR("àá"))) --- print(lpeg.count("äáàa",lpeg.UR(0x0000,0xFFFF))) +-- print(lpeg.counter(lpeg.P("á") + lpeg.P("à"))("äáàa")) +-- print(lpeg.counter(lpeg.UP("áà"))("äáàa")) +-- print(lpeg.counter(lpeg.US("àá"))("äáàa")) +-- print(lpeg.counter(lpeg.UR("aá"))("äáàa")) +-- print(lpeg.counter(lpeg.UR("àá"))("äáàa")) +-- print(lpeg.counter(lpeg.UR(0x0000,0xFFFF))) function lpeg.is_lpeg(p) return p and lpegtype(p) == "pattern" diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-math.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-math.lua index e5668a5db0a..69f0a19e980 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-math.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-math.lua @@ -141,7 +141,7 @@ if not math.ult then local floor = math.floor - function math.tointeger(m,n) + function math.ult(m,n) -- not ok but i'm not motivated to look into it now return floor(m) < floor(n) -- unsigned comparison needed end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-reference.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-reference.lua index 59cbd9ef8ca..736856d2beb 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-reference.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/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 : 2021-05-20 15:57 +-- merge date : 2022-01-21 20:10 do -- begin closure to overcome local limits and interference @@ -2255,9 +2255,12 @@ function io.copydata(source,target,action) flush() end end -function io.savedata(filename,data,joiner) - local f=open(filename,"wb") +function io.savedata(filename,data,joiner,append) + local f=open(filename,append and "ab" or "wb") if f then + if append and joiner and f:seek("end")>0 then + f:write(joiner) + end if type(data)=="table" then f:write(concat(data,joiner or "")) elseif type(data)=="function" then @@ -2791,7 +2794,7 @@ function file.join(one,two,three,...) if not two then return one=="" and one or lpegmatch(reslasher,one) end - if one=="" then + if not one or one=="" then return lpegmatch(stripper,three and concat({ two,three,... },"/") or two) end if lpegmatch(isnetwork,one) then @@ -3099,7 +3102,7 @@ if not math.tointeger then end if not math.ult then local floor=math.floor - function math.tointeger(m,n) + function math.ult(m,n) return floor(m)<floor(n) end end @@ -3118,12 +3121,13 @@ 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,find=string.format,string.gsub,string.rep,string.sub,string.find +local format,gsub,rep,sub,find,char=string.format,string.gsub,string.rep,string.sub,string.find,string.char local load,dump=load,string.dump local tonumber,type,tostring,next,setmetatable=tonumber,type,tostring,next,setmetatable local unpack,concat=table.unpack,table.concat local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc local patterns,lpegmatch=lpeg.patterns,lpeg.match +local tsplitat=lpeg.tsplitat local utfchar,utfbyte,utflen=utf.char,utf.byte,utf.len local loadstripped=function(str,shortcuts) if shortcuts then @@ -3477,6 +3481,14 @@ patterns.escapedquotes=pattern function string.escapedquotes(s) return lpegmatch(pattern,s) end +local pattern=(1-P("\\"))^1;pattern=Cs ( + pattern*((P("\\")/""*(digit^-3/function(s) return char(tonumber(s)) end))+pattern )^1 +) +patterns.unescapedquotes=pattern +function string.unescapedquotes(s) + return lpegmatch(pattern,s) or s +end +string.texnewlines=lpeg.replacer(patterns.newline,"\r",true) local preamble="" local environment={ global=global or _G, @@ -4012,7 +4024,6 @@ function number.to16dot16(n) return f_16_16(n/65536.0) end if not string.explode then - local tsplitat=lpeg.tsplitat local p_utf=patterns.utf8character local p_check=C(p_utf)*(P("+")*Cc(true))^0 local p_split=Ct(C(p_utf)^0) @@ -4032,6 +4043,20 @@ if not string.explode then end end end +do + local p_whitespace=patterns.whitespace^1 + local cache=setmetatable({},{ __index=function(t,k) + local p=tsplitat(p_whitespace*P(k)*p_whitespace) + local v=function(s) + return lpegmatch(p,s) + end + t[k]=v + return v + end }) + function string.wordsplitter(s) + return cache[s] + end +end end -- closure @@ -4202,20 +4227,20 @@ function files.readinteger4le(f) end end function files.readfixed2(f) - local a,b=byte(f:read(2),1,2) - if a>=0x80 then - tonumber((a-0x100).."."..b) - else - tonumber((a ).."."..b) + local n1,n2=byte(f:read(2),1,2) + if n1>=0x80 then + n1=n1-0x100 end + return n1+n2/0xFF end function files.readfixed4(f) local a,b,c,d=byte(f:read(4),1,4) - if a>=0x80 then - tonumber((0x100*a+b-0x10000).."."..(0x100*c+d)) - else - tonumber((0x100*a+b ).."."..(0x100*c+d)) + local n1=0x100*a+b + local n2=0x100*c+d + if n1>=0x8000 then + n1=n1-0x10000 end + return n1+n2/0xFFFF end if bit32 then local extract=bit32.extract @@ -4996,7 +5021,7 @@ nuts.setcomponents=direct.setcomponents nuts.setdirection=direct.setdirection nuts.setdisc=direct.setdisc nuts.setreplace=direct.setreplace -nuts.setfield=setfield +nuts.setfield=direct.setfield nuts.setkern=direct.setkern nuts.setlink=direct.setlink nuts.setlist=direct.setlist @@ -5379,6 +5404,7 @@ characters.classifiers={ [1562]=5, [1563]=6, [1564]=6, + [1565]=6, [1566]=6, [1567]=6, [1568]=2, @@ -5845,9 +5871,9 @@ characters.classifiers={ [2131]=2, [2132]=3, [2133]=2, - [2134]=4, - [2135]=4, - [2136]=4, + [2134]=3, + [2135]=3, + [2136]=3, [2137]=5, [2138]=5, [2139]=5, @@ -5862,6 +5888,47 @@ characters.classifiers={ [2152]=2, [2153]=3, [2154]=3, + [2160]=3, + [2161]=3, + [2162]=3, + [2163]=3, + [2164]=3, + [2165]=3, + [2166]=3, + [2167]=3, + [2168]=3, + [2169]=3, + [2170]=3, + [2171]=3, + [2172]=3, + [2173]=3, + [2174]=3, + [2175]=3, + [2176]=3, + [2177]=3, + [2178]=3, + [2179]=2, + [2180]=2, + [2181]=2, + [2182]=2, + [2183]=4, + [2184]=4, + [2185]=2, + [2186]=2, + [2187]=2, + [2188]=2, + [2189]=2, + [2190]=3, + [2192]=4, + [2193]=4, + [2200]=5, + [2201]=5, + [2202]=5, + [2203]=5, + [2204]=5, + [2205]=5, + [2206]=5, + [2207]=5, [2208]=2, [2209]=2, [2210]=2, @@ -5883,6 +5950,7 @@ characters.classifiers={ [2226]=3, [2227]=2, [2228]=2, + [2229]=2, [2230]=2, [2231]=2, [2232]=2, @@ -5891,6 +5959,26 @@ characters.classifiers={ [2235]=2, [2236]=2, [2237]=2, + [2238]=2, + [2239]=2, + [2240]=2, + [2241]=2, + [2242]=2, + [2243]=2, + [2244]=2, + [2245]=2, + [2246]=2, + [2247]=2, + [2248]=2, + [2250]=5, + [2251]=5, + [2252]=5, + [2253]=5, + [2254]=5, + [2255]=5, + [2256]=5, + [2257]=5, + [2258]=5, [2259]=5, [2260]=5, [2261]=5, @@ -6010,6 +6098,7 @@ characters.classifiers={ [2883]=5, [2884]=5, [2893]=5, + [2901]=5, [2902]=5, [2914]=5, [2915]=5, @@ -6018,6 +6107,7 @@ characters.classifiers={ [3021]=5, [3072]=5, [3076]=5, + [3132]=5, [3134]=5, [3135]=5, [3136]=5, @@ -6051,6 +6141,7 @@ characters.classifiers={ [3405]=5, [3426]=5, [3427]=5, + [3457]=5, [3530]=5, [3538]=5, [3539]=5, @@ -6230,6 +6321,7 @@ characters.classifiers={ [6156]=5, [6157]=5, [6158]=4, + [6159]=5, [6176]=2, [6177]=2, [6178]=2, @@ -6414,6 +6506,22 @@ characters.classifiers={ [6843]=5, [6844]=5, [6845]=5, + [6847]=5, + [6848]=5, + [6849]=5, + [6850]=5, + [6851]=5, + [6852]=5, + [6853]=5, + [6854]=5, + [6855]=5, + [6856]=5, + [6857]=5, + [6858]=5, + [6859]=5, + [6860]=5, + [6861]=5, + [6862]=5, [6912]=5, [6913]=5, [6914]=5, @@ -6546,6 +6654,7 @@ characters.classifiers={ [7671]=5, [7672]=5, [7673]=5, + [7674]=5, [7675]=5, [7676]=5, [7677]=5, @@ -6647,6 +6756,7 @@ characters.classifiers={ [43019]=5, [43045]=5, [43046]=5, + [43052]=5, [43072]=2, [43073]=2, [43074]=2, @@ -6923,6 +7033,8 @@ characters.classifiers={ [68901]=5, [68902]=5, [68903]=5, + [69291]=5, + [69292]=5, [69424]=2, [69425]=2, [69426]=2, @@ -6960,6 +7072,56 @@ characters.classifiers={ [69458]=2, [69459]=2, [69460]=3, + [69488]=2, + [69489]=2, + [69490]=2, + [69491]=2, + [69492]=3, + [69493]=3, + [69494]=2, + [69495]=2, + [69496]=2, + [69497]=2, + [69498]=2, + [69499]=2, + [69500]=2, + [69501]=2, + [69502]=2, + [69503]=2, + [69504]=2, + [69505]=2, + [69506]=5, + [69507]=5, + [69508]=5, + [69509]=5, + [69552]=2, + [69553]=4, + [69554]=2, + [69555]=2, + [69556]=3, + [69557]=3, + [69558]=3, + [69559]=4, + [69560]=2, + [69561]=3, + [69562]=3, + [69563]=2, + [69564]=2, + [69565]=3, + [69566]=2, + [69567]=2, + [69568]=4, + [69569]=2, + [69570]=3, + [69571]=3, + [69572]=2, + [69573]=4, + [69574]=4, + [69575]=4, + [69576]=4, + [69577]=3, + [69578]=2, + [69579]=1, [69633]=5, [69688]=5, [69689]=5, @@ -6976,6 +7138,9 @@ characters.classifiers={ [69700]=5, [69701]=5, [69702]=5, + [69744]=5, + [69747]=5, + [69748]=5, [69759]=5, [69760]=5, [69761]=5, @@ -6986,6 +7151,7 @@ characters.classifiers={ [69817]=5, [69818]=5, [69821]=4, + [69826]=5, [69837]=4, [69888]=5, [69889]=5, @@ -7018,6 +7184,7 @@ characters.classifiers={ [70090]=5, [70091]=5, [70092]=5, + [70095]=5, [70191]=5, [70192]=5, [70193]=5, @@ -7128,6 +7295,10 @@ characters.classifiers={ [71735]=5, [71737]=5, [71738]=5, + [71995]=5, + [71996]=5, + [71998]=5, + [72003]=5, [72148]=5, [72149]=5, [72150]=5, @@ -7265,8 +7436,78 @@ characters.classifiers={ [94096]=5, [94097]=5, [94098]=5, + [94180]=5, [113821]=5, [113822]=5, + [118528]=5, + [118529]=5, + [118530]=5, + [118531]=5, + [118532]=5, + [118533]=5, + [118534]=5, + [118535]=5, + [118536]=5, + [118537]=5, + [118538]=5, + [118539]=5, + [118540]=5, + [118541]=5, + [118542]=5, + [118543]=5, + [118544]=5, + [118545]=5, + [118546]=5, + [118547]=5, + [118548]=5, + [118549]=5, + [118550]=5, + [118551]=5, + [118552]=5, + [118553]=5, + [118554]=5, + [118555]=5, + [118556]=5, + [118557]=5, + [118558]=5, + [118559]=5, + [118560]=5, + [118561]=5, + [118562]=5, + [118563]=5, + [118564]=5, + [118565]=5, + [118566]=5, + [118567]=5, + [118568]=5, + [118569]=5, + [118570]=5, + [118571]=5, + [118572]=5, + [118573]=5, + [118576]=5, + [118577]=5, + [118578]=5, + [118579]=5, + [118580]=5, + [118581]=5, + [118582]=5, + [118583]=5, + [118584]=5, + [118585]=5, + [118586]=5, + [118587]=5, + [118588]=5, + [118589]=5, + [118590]=5, + [118591]=5, + [118592]=5, + [118593]=5, + [118594]=5, + [118595]=5, + [118596]=5, + [118597]=5, + [118598]=5, [119143]=5, [119144]=5, [119145]=5, @@ -7464,6 +7705,7 @@ characters.classifiers={ [123188]=5, [123189]=5, [123190]=5, + [123566]=5, [123628]=5, [123629]=5, [123630]=5, @@ -7631,6 +7873,7 @@ characters.indicgroups={ [2879]=true, [2902]=true, [3376]=true, + [5901]=true, }, ["after_postscript"]={ [2433]=true, @@ -7806,6 +8049,7 @@ characters.indicgroups={ [2915]=true, [3009]=true, [3010]=true, + [3132]=true, [3170]=true, [3171]=true, [3260]=true, @@ -8224,6 +8468,7 @@ characters.indicgroups={ [4225]=true, [4226]=true, [4238]=true, + [5901]=true, [43488]=true, [43489]=true, [43490]=true, @@ -8548,6 +8793,7 @@ characters.indicgroups={ [3090]=true, [3091]=true, [3092]=true, + [3165]=true, [3168]=true, [3169]=true, [3205]=true, @@ -8564,6 +8810,7 @@ characters.indicgroups={ [3218]=true, [3219]=true, [3220]=true, + [3293]=true, [3296]=true, [3297]=true, [3333]=true, @@ -8604,6 +8851,7 @@ characters.indicgroups={ [2620]=true, [2748]=true, [2876]=true, + [3132]=true, [3260]=true, }, ["post_mark"]={ @@ -8707,6 +8955,7 @@ characters.indicgroups={ [3120]=true, [3248]=true, [3376]=true, + [5901]=true, }, ["stress_tone_mark"]={ [2385]=true, @@ -10252,10 +10501,16 @@ local encodings={} fonts.encodings=encodings encodings.agl={} encodings.known={} +encodings.glyphlistfilename="font-age.lua" setmetatable(encodings.agl,{ __index=function(t,k) if k=="unicodes" then logs.report("fonts","loading (extended) adobe glyph list") - local unicodes=dofile(resolvers.findfile("font-age.lua")) + local foundname=resolvers.findfile(encodings.glyphlistfilename) or "" + local unicodes=foundname~="" and dofile(foundname) + if type(unicodes)~="table" then + logs.report("fonts","missing or invalid (extended) adobe glyph list") + unicodes={} + end encodings.agl={ unicodes=unicodes } return unicodes else @@ -11287,15 +11542,15 @@ local weights={ [900]="black", } local widths={ - [1]="ultracondensed", - [2]="extracondensed", - [3]="condensed", - [4]="semicondensed", - [5]="normal", - [6]="semiexpanded", - [7]="expanded", - [8]="extraexpanded", - [9]="ultraexpanded", + "ultracondensed", + "extracondensed", + "condensed", + "semicondensed", + "normal", + "semiexpanded", + "expanded", + "extraexpanded", + "ultraexpanded", } setmetatableindex(weights,function(t,k) local r=floor((k+50)/100)*100 @@ -11305,30 +11560,28 @@ end) setmetatableindex(widths,function(t,k) return "normal" end) -local panoseweights={ - [ 0]="normal", - [ 1]="normal", - [ 2]="verylight", - [ 3]="light", - [ 4]="thin", - [ 5]="book", - [ 6]="medium", - [ 7]="demi", - [ 8]="bold", - [ 9]="heavy", - [10]="black", +local panoseweights={ [0]="normal", + "normal", + "verylight", + "light", + "thin", + "book", + "medium", + "demi", + "bold", + "heavy", + "black", } -local panosewidths={ - [ 0]="normal", - [ 1]="normal", - [ 2]="normal", - [ 3]="normal", - [ 4]="normal", - [ 5]="expanded", - [ 6]="condensed", - [ 7]="veryexpanded", - [ 8]="verycondensed", - [ 9]="monospaced", +local panosewidths={ [0]="normal", + "normal", + "normal", + "normal", + "normal", + "expanded", + "condensed", + "veryexpanded", + "verycondensed", + "monospaced", } local helpers={} readers.helpers=helpers @@ -11762,7 +12015,7 @@ readers.post=function(f,fontdata,specification) local version=readulong(f) fontdata.postscript={ version=version, - italicangle=round(1000*readfixed(f))/1000, + italicangle=readfixed(f), underlineposition=readfword(f), underlinethickness=readfword(f), monospaced=readulong(f), @@ -12379,6 +12632,7 @@ local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo,inst pfmwidth=metrics.widthclass or 5, panosewidth=metrics.panosewidth, panoseweight=metrics.panoseweight, + fstype=metrics.fstype or 0, italicangle=postscript.italicangle or 0, units=fontheader.units or 0, designsize=fontdata.designsize, @@ -12872,6 +13126,9 @@ end function readers.compact(fontdata) report("the %a helper is not yet implemented","compact") end +function readers.condense(fontdata) + report("the %a helper is not yet implemented","condense") +end local extenders={} function readers.registerextender(extender) extenders[#extenders+1]=extender @@ -13339,7 +13596,8 @@ do top=0 end local p_dictionary=( - p_byte+p_positive+p_negative+p_short+p_long+p_nibbles+p_single+p_double+p_unsupported + p_byte+p_positive+p_negative+p_short+p_long+p_nibbles+p_single+p_double ++p_unsupported )^1 parsedictionaries=function(data,dictionaries,version) stack={} @@ -13411,6 +13669,7 @@ do local y=0 local width=false local lsb=0 +local result={} local r=0 local stems=0 local globalbias=0 @@ -14365,12 +14624,12 @@ do stack[top]=-t*256+64148-tab[i+1] i=i+2 else - local n=0x100*tab[i+1]+tab[i+2] - if n>=0x8000 then - stack[top]=n-0x10000+(0x100*tab[i+3]+tab[i+4])/0xFFFF - else - stack[top]=n+(0x100*tab[i+3]+tab[i+4])/0xFFFF + local n1=0x100*tab[i+1]+tab[i+2] + local n2=0x100*tab[i+3]+tab[i+4] + if n1>=0x8000 then + n1=n1-0x10000 end + stack[top]=n1+n2/0xFFFF i=i+5 end elseif t==28 then @@ -14458,7 +14717,7 @@ do i=i+s+1 elseif t==1 or t==3 or t==18 or operation==23 then p_getstem() - if true then + if version=="cff" then if top>0 then for i=1,top do r=r+1;result[r]=encode[stack[i]] @@ -14471,7 +14730,7 @@ do end i=i+1 elseif t==19 or t==20 then - local s=p_getmask() or 0 + local s=p_getmask() or 0 if true then if top>0 then for i=1,top do @@ -14494,7 +14753,7 @@ do i=i+1 elseif t==13 then hsbw() - if version=="cff" then + if true then r=r+1;result[r]=encode[lsb] r=r+1;result[r]=chars[22] else @@ -14621,6 +14880,7 @@ do r=r+1 result[r]=c_endchar local stream=concat(result) +result=nil if glyph then glyph.stream=stream else @@ -14642,6 +14902,7 @@ do width=width, name=charset and charset[index] or nil, } +result=nil else glyphs[index]={ boundingbox=boundingbox, @@ -15128,7 +15389,7 @@ if not modules then modules={} end modules ['font-ttf']={ } local next,type,unpack=next,type,unpack local band,rshift=bit32.band,bit32.rshift -local sqrt,round=math.sqrt,math.round +local sqrt,round,abs,min,max=math.sqrt,math.round,math.abs,math.min,math.max local char,rep=string.char,string.rep local concat=table.concat local idiv=number.idiv @@ -15262,69 +15523,164 @@ local function curveto(m_x,m_y,l_x,l_y,r_x,r_y) r_x+2/3*(m_x-r_x),r_y+2/3*(m_y-r_y), r_x,r_y,"c" end +local xv={} +local yv={} local function applyaxis(glyph,shape,deltas,dowidth) local points=shape.points if points then local nofpoints=#points - local h=nofpoints+2 - local l=nofpoints+1 local dw=0 local dl=0 for i=1,#deltas do local deltaset=deltas[i] local xvalues=deltaset.xvalues local yvalues=deltaset.yvalues - local dpoints=deltaset.points - local factor=deltaset.factor - if dpoints then - local nofdpoints=#dpoints - for i=1,nofdpoints do - local d=dpoints[i] - local p=points[d] - if p then - if xvalues then - local x=xvalues[i] - if x and x~=0 then - p[1]=p[1]+factor*x + if xvalues and yvalues then + local dpoints=deltaset.points + local factor=deltaset.factor + if dpoints then + local cnt=#dpoints + if dowidth then + cnt=cnt-4 + end + if cnt==1 then + local d=dpoints[1] + local x=xvalues[d]*factor + local y=yvalues[d]*factor + for i=1,nofpoints do + local p=points[i] + if x~=0 then + p[1]=p[1]+x + end + if y~=0 then + p[2]=p[2]+y end end - if yvalues then - local y=yvalues[i] - if y and y~=0 then - p[2]=p[2]+factor*y + elseif cnt>0 then + local function find(i) + local prv=cnt + for j=1,cnt do + local nxt=dpoints[j] + if nxt==i then + return false,j,false + elseif nxt>i then + return prv,false,j + end + prv=j + end + return prv,false,1 + end + for i=1,nofpoints do + local d1,d2,d3=find(i) + local p2=points[i] + if d2 then + xv[i]=xvalues[d2] + yv[i]=yvalues[d2] + else + local n1=dpoints[d1] + local n3=dpoints[d3] + if n1>nofpoints then + n1=nofpoints + end + if n3>nofpoints then + n3=nofpoints + end + local p1=points[n1] + local p3=points[n3] + local p1x=p1[1] + local p2x=p2[1] + local p3x=p3[1] + local p1y=p1[2] + local p2y=p2[2] + local p3y=p3[2] + local x1=xvalues[d1] + local y1=yvalues[d1] + local x3=xvalues[d3] + local y3=yvalues[d3] + local fx + local fy + if p1x==p3x then + if x1==x3 then + fx=x1 + else + fx=0 + end + elseif p2x<=min(p1x,p3x) then + if p1x<p3x then + fx=x1 + else + fx=x3 + end + elseif p2x>=max(p1x,p3x) then + if p1x>p3x then + fx=x1 + else + fx=x3 + end + else + fx=(p2x-p1x)/(p3x-p1x) + fx=(1-fx)*x1+fx*x3 + end + if p1y==p3y then + if y1==y3 then + fy=y1 + else + fy=0 + end + elseif p2y<=min(p1y,p3y) then + if p1y<p3y then + fy=y1 + else + fy=y3 + end + elseif p2y>=max(p1y,p3y) then + if p1y>p3y then + fy=y1 + else + fy=y3 + end + else + fy=(p2y-p1y)/(p3y-p1y) + fy=(1-fy)*y1+fy*y3 + end + xv[i]=fx + yv[i]=fy end end - elseif dowidth then - if d==h then - local x=xvalues[i] - if x then - dw=dw+factor*x + for i=1,nofpoints do + local pi=points[i] + local fx=xv[i] + local fy=yv[i] + if fx~=0 then + pi[1]=pi[1]+factor*fx end - elseif d==l then - local x=xvalues[i] - if x then - dl=dl+factor*x + if fy~=0 then + pi[2]=pi[2]+factor*fy end end + else + report("bad deltapoint data, maybe a missing hvar table") end - end - else - for i=1,nofpoints do - local p=points[i] - if xvalues then + else + for i=1,nofpoints do + local p=points[i] local x=xvalues[i] - if x and x~=0 then - p[1]=p[1]+factor*x - end - end - if yvalues then - local y=yvalues[i] - if y and y~=0 then - p[2]=p[2]+factor*y + if x then + local y=yvalues[i] + if x~=0 then + p[1]=p[1]+factor*x + end + if y~=0 then + p[2]=p[2]+factor*y + end + else + break end end end if dowidth then + local h=nofpoints+2 + local l=nofpoints+1 local x=xvalues[h] if x then dw=dw+factor*x @@ -15903,13 +16259,13 @@ local function readcomposite(f) yoffset=yoffset*yscale end elseif band(flags,0x0080)~=0 then - xscale=read2dot14(f) - xrotate=read2dot14(f) - yrotate=read2dot14(f) - yscale=read2dot14(f) + xscale=read2dot14(f) + xrotate=read2dot14(f) + yrotate=read2dot14(f) + yscale=read2dot14(f) if f_xyarg and f_offset then - xoffset=xoffset*sqrt(xscale^2+xrotate^2) - yoffset=yoffset*sqrt(yrotate^2+yscale^2) + xoffset=xoffset*sqrt(xscale^2+yrotate^2) + yoffset=yoffset*sqrt(xrotate^2+yscale^2) end end nofcomponents=nofcomponents+1 @@ -16067,40 +16423,6 @@ end local function readdeltas(f,nofpoints) local deltas={} local p=0 - local z=0 - while nofpoints>0 do - local control=readbyte(f) -if not control then - break -end - local allzero=band(control,0x80)~=0 - local runlength=band(control,0x3F)+1 - if allzero then - z=z+runlength - else - local runreader=band(control,0x40)~=0 and readshort or readinteger - if z>0 then - for i=1,z do - p=p+1 - deltas[p]=0 - end - z=0 - end - for i=1,runlength do - p=p+1 - deltas[p]=runreader(f) - end - end - nofpoints=nofpoints-runlength - end - if p>0 then - return deltas - else - end -end -local function readdeltas(f,nofpoints) - local deltas={} - local p=0 while nofpoints>0 do local control=readbyte(f) if control then @@ -19458,6 +19780,7 @@ function readers.hvar(f,fontdata,specification) end local tableoffset=gotodatatable(f,fontdata,"hvar",specification.variable) if not tableoffset then + report("no hvar table, expect problems due to messy widths") return end local version=readulong(f) @@ -20842,7 +21165,7 @@ if not modules then modules={} end modules ['font-otl']={ local lower=string.lower local type,next,tonumber,tostring,unpack=type,next,tonumber,tostring,unpack local abs=math.abs -local derivetable=table.derive +local derivetable,sortedhash=table.derive,table.sortedhash local formatters=string.formatters local setmetatableindex=table.setmetatableindex local allocate=utilities.storage.allocate @@ -20858,7 +21181,7 @@ local trace_defining=false registertracker("fonts.defining",function(v) trace_d local report_otf=logs.reporter("fonts","otf loading") local fonts=fonts local otf=fonts.handlers.otf -otf.version=3.116 +otf.version=3.119 otf.cache=containers.define("fonts","otl",otf.version,true) otf.svgcache=containers.define("fonts","svg",otf.version,true) otf.pngcache=containers.define("fonts","png",otf.version,true) @@ -20993,6 +21316,9 @@ function otf.load(filename,sub,instance) if cleanup==0 then checkmemory(used,threshold,tracememory) end + if context then + otfreaders.condense(data) + end otfreaders.pack(data) report_otf("loading done") report_otf("saving %a in cache",filename) @@ -21063,7 +21389,7 @@ local function copytotfm(data,cache_id) local properties=derivetable(data.properties) local descriptions=derivetable(data.descriptions) local goodies=derivetable(data.goodies) - local characters={} + local characters={} local parameters={} local mathparameters={} local resources=data.resources @@ -21231,7 +21557,25 @@ local function copytotfm(data,cache_id) properties.name=filename or fullname properties.subfont=subfont if not CONTEXTLMTXMODE or CONTEXTLMTXMODE==0 then - properties.encodingbytes=2 + properties.encodingbytes=2 +elseif CONTEXTLMTXMODE then + local duplicates=resources and resources.duplicates + if duplicates then + local maxindex=data.nofglyphs or metadata.nofglyphs + if maxindex then + for u,d in sortedhash(duplicates) do + local du=descriptions[u] + if du then + for uu in sortedhash(d) do + maxindex=maxindex+1 + descriptions[uu].dupindex=du.index + descriptions[uu].index=maxindex + end + else + end + end + end + end end properties.private=properties.private or data.private or privateoffset return { @@ -21599,7 +21943,7 @@ local function gref(descriptions,n) end end local function cref(feature,sequence) - return formatters["feature %a, type %a, chain lookup %a"](feature,sequence.type,sequence.name) + return formatters["feature %a, type %a, (chain) lookup %a"](feature,sequence.type,sequence.name) end local function report_substitution(feature,sequence,descriptions,unicode,substitution) if unicode==substitution then @@ -21660,49 +22004,60 @@ end local function makefake(tfmdata,name,present) local private=getprivate(tfmdata) local character={ intermediate=true,ligatures={} } - resources.unicodes[name]=private + tfmdata.resources.unicodes[name]=private tfmdata.characters[private]=character tfmdata.descriptions[private]={ name=name } present[name]=private return character end local function make_1(present,tree,name) - for k,v in next,tree do - if k=="ligature" then - present[name]=v + if tonumber(tree) then + present[name]=v + else + for k,v in next,tree do + if k=="ligature" then + present[name]=v + else + make_1(present,v,name.."_"..k) + end + end + end +end +local function make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,v) + local character=characters[preceding] + if not character then + if trace_baseinit then + report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding) + end + character=makefake(tfmdata,name,present) + end + local ligatures=character.ligatures + if ligatures then + ligatures[unicode]={ char=v } + else + character.ligatures={ [unicode]={ char=v } } + end + if done then + local d=done[name] + if not d then + done[name]={ "dummy",v } else - make_1(present,v,name.."_"..k) + d[#d+1]=v end end end local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done) - for k,v in next,tree do - if k=="ligature" then - local character=characters[preceding] - if not character then - if trace_baseinit then - report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding) - end - character=makefake(tfmdata,name,present) - end - local ligatures=character.ligatures - if ligatures then - ligatures[unicode]={ char=v } + if tonumber(tree) then + make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,tree) + else + for k,v in next,tree do + if k=="ligature" then + make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,v) else - character.ligatures={ [unicode]={ char=v } } - end - if done then - local d=done[name] - if not d then - done[name]={ "dummy",v } - else - d[#d+1]=v - end + local code=present[name] or unicode + local name=name.."_"..k + make_2(present,tfmdata,characters,v,name,code,k,done) end - else - local code=present[name] or unicode - local name=name.."_"..k - make_2(present,tfmdata,characters,v,name,code,k,done) end end end @@ -23522,9 +23877,9 @@ local f_index=formatters["I%05X"] local f_character_y=formatters["%C"] local f_character_n=formatters["[ %C ]"] local check_duplicates=true -local check_soft_hyphen=true +local check_soft_hyphen=context directives.register("otf.checksofthyphen",function(v) - check_soft_hyphen=v + check_soft_hyphen=v end) local function replaced(list,index,replacement) if type(list)=="number" then @@ -23913,27 +24268,30 @@ local function copyduplicates(fontdata) local resources=fontdata.resources local duplicates=resources.duplicates if check_soft_hyphen then - local ds=descriptions[0xAD] - if not ds or ds.width==0 then - if ds then + local dh=descriptions[0x2D] + if dh then + local ds=descriptions[0xAD] + if not ds or ds.width~=dh.width then descriptions[0xAD]=nil - if trace_unicodes then - report_unicodes("patching soft hyphen") + if ds then + if trace_unicodes then + report_unicodes("patching soft hyphen") + end + else + if trace_unicodes then + report_unicodes("adding soft hyphen") + end end - else - if trace_unicodes then - report_unicodes("adding soft hyphen") + if not duplicates then + duplicates={} + resources.duplicates=duplicates + end + local d=duplicates[0x2D] + if d then + d[0xAD]=true + else + duplicates[0x2D]={ [0xAD]=true } end - end - if not duplicates then - duplicates={} - resources.duplicates=duplicates - end - local dh=duplicates[0x2D] - if dh then - dh[#dh+1]={ [0xAD]=true } - else - duplicates[0x2D]={ [0xAD]=true } end end end @@ -24199,6 +24557,7 @@ local function unifyglyphs(fontdata,usenames) local resources=fontdata.resources local zero=glyphs[0] local zerocode=zero.unicode + local nofglyphs=#glyphs if not zerocode then zerocode=private zero.unicode=zerocode @@ -24213,7 +24572,7 @@ local function unifyglyphs(fontdata,usenames) indices[0]=zerocode end if names then - for index=1,#glyphs do + for index=1,nofglyphs do local glyph=glyphs[index] local unicode=glyph.unicode if not unicode then @@ -24246,7 +24605,7 @@ local function unifyglyphs(fontdata,usenames) descriptions[unicode]=glyph end elseif trace_unicodes then - for index=1,#glyphs do + for index=1,nofglyphs do local glyph=glyphs[index] local unicode=glyph.unicode if not unicode then @@ -24287,7 +24646,7 @@ local function unifyglyphs(fontdata,usenames) descriptions[unicode]=glyph end else - for index=1,#glyphs do + for index=1,nofglyphs do local glyph=glyphs[index] local unicode=glyph.unicode if not unicode then @@ -24313,7 +24672,7 @@ local function unifyglyphs(fontdata,usenames) descriptions[unicode]=glyph end end - for index=1,#glyphs do + for index=1,nofglyphs do local math=glyphs[index].math if math then local list=math.vparts @@ -24336,7 +24695,7 @@ local function unifyglyphs(fontdata,usenames) end local colorpalettes=resources.colorpalettes if colorpalettes then - for index=1,#glyphs do + for index=1,nofglyphs do local colors=glyphs[index].colors if colors then for i=1,#colors do @@ -24351,6 +24710,7 @@ local function unifyglyphs(fontdata,usenames) fontdata.names=names fontdata.descriptions=descriptions fontdata.hashmethod=hashmethod + fontdata.nofglyphs=nofglyphs return indices,names end local p_crappyname do @@ -24423,6 +24783,10 @@ function readers.getcomponents(fontdata) local function traverse(p,k,v) if k=="ligature" then collected[v]={ unpack(l) } + elseif tonumber(v) then + insert(l,k) + collected[v]={ unpack(l) } + remove(l) else insert(l,k) for k,vv in next,v do @@ -24583,7 +24947,6 @@ local function tabstr_flat(t) end end local function tabstr_mixed(t) - local s={} local n=#t if n==0 then return "" @@ -24597,6 +24960,7 @@ local function tabstr_mixed(t) return tostring(k) end else + local s={} for i=1,n do local k=t[i] if k==true then @@ -25920,6 +26284,85 @@ function readers.compact(data) end end end +if CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 then + local done=0 + local function condense_1(k,v,t) + if type(v)=="table" then + local u=false + local l=false + for k,v in next,v do + if k=="ligature" then + l=v + if u then + break + end + elseif u then + break + else + u=true + end + end + if l and not u then + t[k]=l + done=done+1 + end + if u then + for k,vv in next,v do + if k~="ligature" then + condense_1(k,vv,v) + end + end + end + end + end + local function condensesteps_1(lookup) + done=0 + if lookup.type=="gsub_ligature" then + local steps=lookup.steps + if steps then + for i=1,#steps do + local step=steps[i] + local coverage=step.coverage + if coverage then + for k,v in next,coverage do + if condense_1(k,v,coverage) then + coverage[k]=v.ligature + done=done+1 + end + end + end + end + end + end + return done + end + function readers.condense(data) + if not data or data.condensed then + return + else + data.condensed=true + end + local resources=data.resources + local condensed=0 + local function condense(what) + local lookups=resources[what] + if lookups then + for i=1,#lookups do + condensed=condensed+condensesteps_1(lookups[i]) + end + elseif trace_optimizations then + report_optimizations("no lookups in %a",what) + end + end + condense("sequences") + condense("sublookups") + if trace_optimizations then + if condensed>0 then + report_optimizations("%i ligatures condensed",condensed) + end + end + end +end local function mergesteps(t,k) if k=="merged" then local merged={} @@ -26024,7 +26467,7 @@ function readers.expand(data) end end end - local function expandlookups(sequences) + local function expandlookups(sequences,whatever) if sequences then for i=1,#sequences do local sequence=sequences[i] @@ -26097,6 +26540,9 @@ function readers.expand(data) if lookups then for k,v in next,lookups do local lookup=sublookups[v] +if not lookup and whatever then + lookup=whatever[v] +end if lookup then lookups[k]=lookup if not subtype then @@ -26155,7 +26601,7 @@ function readers.expand(data) end end expandlookups(sequences) - expandlookups(sublookups) + expandlookups(sublookups,sequences) end end -- closure @@ -26698,6 +27144,7 @@ local trace_compruns=false registertracker("otf.compruns",function(v) trace_com local trace_testruns=false registertracker("otf.testruns",function(v) trace_testruns=v end) local forcediscretionaries=false local forcepairadvance=false +local repeatablemultiples=context or false directives.register("otf.forcediscretionaries",function(v) forcediscretionaries=v end) @@ -27076,15 +27523,47 @@ local function multiple_glyphs(head,start,multiple,skiphash,what,stop) insertnodeafter(head,start,n) start=n end - if what==true then - elseif what>1 then - local m=multiple[nofmultiples] - for i=2,what do - local n=copy_node(start) - resetinjection(n) - setchar(n,m) - insertnodeafter(head,start,n) - start=n + if what~=true and repeatablemultiples then + local kind=type(what) + local m,f,l + if kind=="string" then + local what,n=string.match(what,"^repeat(.-)[:=](%d+)$") + if what=="middle" then + m=tonumber(n) + elseif what=="first" then + f=tonumber(n) + elseif what=="last" then + l=tonumber(n) + end + elseif kind=="table" then + m=what.middle + f=what.first + l=what.last + end + if f or m or l then + if m and m>1 and nofmultiples==3 then + local middle=getnext(first) + for i=2,m do + local n=copynode(middle) + resetinjection(n) + insertnodeafter(head,first,n) + end + end + if f and f>1 then + for i=2,f do + local n=copynode(first) + resetinjection(n) + insertnodeafter(head,first,n) + end + end + if l and l>1 then + for i=2,l do + local n=copynode(start) + resetinjection(n) + insertnodeafter(head,start,n) + start=n + end + end end end end @@ -27172,7 +27651,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip while current do local char=ischar(current,currentfont) if char then - local lg=ligature[char] + local lg=not tonumber(ligature) and ligature[char] if lg then stop=current ligature=lg @@ -27185,14 +27664,14 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip end end if stop then - local lig=ligature.ligature - if lig then + local ligature=tonumber(ligature) or ligature.ligature + if ligature then if trace_ligatures then local stopchar=getchar(stop) - head,start=markstoligature(head,start,stop,lig) + head,start=markstoligature(head,start,stop,ligature) logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(getchar(start))) else - head,start=markstoligature(head,start,stop,lig) + head,start=markstoligature(head,start,stop,ligature) end return head,start,true,false else @@ -27207,7 +27686,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip if skiphash and skiphash[char] then current=getnext(current) else - local lg=ligature[char] + local lg=not tonumber(ligature) and ligature[char] if lg then if marks[char] then hasmarks=true @@ -27233,20 +27712,20 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip local match if replace then local char=ischar(replace,currentfont) - if char and ligature[char] then + if char and (not tonumber(ligature) and ligature[char]) then match=true end end if not match and pre then local char=ischar(pre,currentfont) - if char and ligature[char] then + if char and (not tonumber(ligature) and ligature[char]) then match=true end end if not match and not pre or not replace then local n=getnext(discfound) local char=ischar(n,currentfont) - if char and ligature[char] then + if char and (not tonumber(ligature) and ligature[char]) then match=true end end @@ -27289,21 +27768,21 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip return head,start,true,true end end - local lig=ligature.ligature - if lig then + local ligature=tonumber(ligature) or ligature.ligature + if ligature then if stop then if trace_ligatures then local stopchar=getchar(stop) - head,start=toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks) - logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(lig)) + head,start=toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks) + logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(ligature)) else - head,start=toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks) + head,start=toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks) end else resetinjection(start) - setchar(start,lig) + setchar(start,ligature) if trace_ligatures then - logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(lig)) + logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(ligature)) end end return head,start,true,false @@ -27498,7 +27977,7 @@ function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlm end end elseif trace_bugs then - onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + onetimemessage(currentfont,basechar,"no base anchors") end elseif trace_bugs then logwarning("%s: prev node is no char, case %i",pref(dataset,sequence),1) @@ -27777,7 +28256,7 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup if skiphash and skiphash[schar] then current=getnext(current) else - local lg=ligatures[schar] + local lg=not tonumber(ligatures) and ligatures[schar] if lg then ligatures=lg last=current @@ -27796,7 +28275,7 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup end end end - local ligature=ligatures.ligature + local ligature=tonumber(ligatures) or ligatures.ligature if ligature then if chainindex then stop=last @@ -28138,7 +28617,7 @@ function chainprocs.gpos_cursive(head,start,stop,dataset,sequence,currentlookup, end end elseif trace_bugs then - onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) + onetimemessage(currentfont,startchar,"no entry anchors") end break end @@ -28208,7 +28687,7 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) local chainproc=chainprocs[chainkind] if chainproc then local ok - head,start,ok=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash) + head,start,ok=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,1) if ok then done=true end @@ -28625,6 +29104,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s local ck=contexts[k] local seq=ck[3] local f=ck[4] +local last=start if not startchar or not seq[f][startchar] then goto next end @@ -28633,7 +29113,6 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s else local l=ck[5] local current=start - local last=start if l>f then local discfound local n=f+1 @@ -29417,7 +29896,7 @@ local function t_run_single(start,stop,font,attr,lookupcache) while s do local char=ischar(s,font) if char then - local lg=lookupmatch[char] + local lg=not tonumber(lookupmatch) and lookupmatch[char] if lg then if sstop then d=1 @@ -29447,7 +29926,7 @@ local function t_run_single(start,stop,font,attr,lookupcache) break end end - if l and l.ligature then + if l and (tonumber(l) or l.ligature) then lastd=d end else @@ -29572,7 +30051,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps) while s do local char=ischar(s) if char then - local lg=lookupmatch[char] + local lg=not tonumber(lookupmatch) and lookupmatch[char] if lg then if sstop then d=1 @@ -29602,7 +30081,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps) break end end - if l and l.ligature then + if l and (tonumber(l) or l.ligature) then lastd=d end end @@ -30378,6 +30857,7 @@ local function validspecification(specification,name) else dataset={ { data=specification.data } } specification.data=nil + specification.coverage=dataset specification.dataset=dataset end local first=dataset[1] @@ -30677,7 +31157,28 @@ local function addfeature(data,feature,specifications) return coverage end local prepare_single=prepare_pair - local function prepare_chain(list,featuretype,sublookups) + local function hassteps(lookups) + if lookups then + for i=1,#lookups do + local l=lookups[i] + if l then + for j=1,#l do + local l=l[j] + if l then + local n=l.nofsteps + if not n then + return true + elseif n>0 then + return true + end + end + end + end + end + end + return false + end + local function prepare_chain(list,featuretype,sublookups,nocheck) local rules=list.rules local coverage={} if rules then @@ -30713,6 +31214,7 @@ local function addfeature(data,feature,specifications) local lookups=rule.lookups or false local subtype=nil if lookups and sublookups then + local l={} for k,v in sortedhash(lookups) do local t=type(v) if t=="table" then @@ -30722,55 +31224,63 @@ local function addfeature(data,feature,specifications) v[i]={ vi } end end + l[k]=v elseif t=="number" then local lookup=sublookups[v] if lookup then - lookups[k]={ lookup } + l[k]={ lookup } if not subtype then subtype=lookup.type end elseif v==0 then - lookups[k]={ { type="gsub_remove" } } + l[k]={ { type="gsub_remove",nosteps=true } } else - lookups[k]=false + l[k]=false end else - lookups[k]=false + l[k]=false end end + if nocheck then + rule.lookups=l + end + lookups=l end if nofsequences>0 then - local hashed={} - for i=1,nofsequences do - local t={} - local s=sequence[i] - for i=1,#s do - local u=tounicode(s[i]) - if u then - t[u]=true + if hassteps(lookups) then + local hashed={} + for i=1,nofsequences do + local t={} + local s=sequence[i] + for i=1,#s do + local u=tounicode(s[i]) + if u then + t[u]=true + end + end + hashed[i]=t + end + sequence=hashed + rulesize=rulesize+1 + rulehash[rulesize]={ + nofrules, + lookuptype, + sequence, + start, + stop, + lookups, + replacements, + subtype, + } + for unic in sortedhash(sequence[start]) do + local cu=coverage[unic] + if not cu then + coverage[unic]=rulehash end end - hashed[i]=t - end - sequence=hashed - rulesize=rulesize+1 - rulehash[rulesize]={ - nofrules, - lookuptype, - sequence, - start, - stop, - lookups, - replacements, - subtype, - } - for unic in sortedhash(sequence[start]) do - local cu=coverage[unic] - if not cu then - coverage[unic]=rulehash - end + sequence.n=nofsequences + else end - sequence.n=nofsequences end end rulehash.n=rulesize @@ -30857,126 +31367,148 @@ local function addfeature(data,feature,specifications) end local askedfeatures=specification.features or everywhere local askedsteps=specification.steps or specification.subtables or { specification.data } or {} - local featuretype=normalized[specification.type or "substitution"] or "substitution" + local featuretype=specification.type or "substitution" + local featureaction=false local featureflags=specification.flags or noflags local nocheck=specification.nocheck + local mapping=specification.mapping local featureorder=specification.order or { feature } local featurechain=(featuretype=="chainsubstitution" or featuretype=="chainposition") and 1 or 0 local nofsteps=0 local steps={} local sublookups=specification.lookups local category=nil + local steptype=nil + local sequence=nil + if fonts.handlers.otf.handlers[featuretype] then + featureaction=true + else + featuretype=normalized[specification.type or "substitution"] or "substitution" + end checkflags(specification,resources) - if sublookups then - local s={} - for i=1,#sublookups do - local specification=sublookups[i] - local askedsteps=specification.steps or specification.subtables or { specification.data } or {} - local featuretype=normalized[specification.type or "substitution"] or "substitution" - local featureflags=specification.flags or noflags - local nofsteps=0 - local steps={} - for i=1,#askedsteps do - local list=askedsteps[i] - local coverage=nil - local format=nil - if featuretype=="substitution" then - coverage=prepare_substitution(list,featuretype,nocheck) - elseif featuretype=="ligature" then - coverage=prepare_ligature(list,featuretype,nocheck) - elseif featuretype=="alternate" then - coverage=prepare_alternate(list,featuretype,nocheck) - elseif featuretype=="multiple" then - coverage=prepare_multiple(list,featuretype,nocheck) - elseif featuretype=="kern" or featuretype=="move" then - format=featuretype - coverage=prepare_kern(list,featuretype) - elseif featuretype=="pair" then - format="pair" - coverage=prepare_pair(list,featuretype) - elseif featuretype=="single" then - format="single" - coverage=prepare_single(list,featuretype) - end - if coverage and next(coverage) then - nofsteps=nofsteps+1 - steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) + for k,v in next,askedfeatures do + if v[1] then + askedfeatures[k]=tohash(v) + end + end + if featureflags[1] then featureflags[1]="mark" end + if featureflags[2] then featureflags[2]="ligature" end + if featureflags[3] then featureflags[3]="base" end + if featureaction then + category="gsub" + sequence={ + features={ [feature]=askedfeatures }, + flags=featureflags, + name=feature, + order=featureorder, + type=featuretype, + nofsteps=0, + } + else + if sublookups then + local s={} + for i=1,#sublookups do + local specification=sublookups[i] + local askedsteps=specification.steps or specification.subtables or { specification.data } or {} + local featuretype=normalized[specification.type or "substitution"] or "substitution" + local featureflags=specification.flags or noflags + local nofsteps=0 + local steps={} + for i=1,#askedsteps do + local list=askedsteps[i] + local coverage=nil + local format=nil + if featuretype=="substitution" then + coverage=prepare_substitution(list,featuretype,nocheck) + elseif featuretype=="ligature" then + coverage=prepare_ligature(list,featuretype,nocheck) + elseif featuretype=="alternate" then + coverage=prepare_alternate(list,featuretype,nocheck) + elseif featuretype=="multiple" then + coverage=prepare_multiple(list,featuretype,nocheck) + elseif featuretype=="kern" or featuretype=="move" then + format=featuretype + coverage=prepare_kern(list,featuretype) + elseif featuretype=="pair" then + format="pair" + coverage=prepare_pair(list,featuretype) + elseif featuretype=="single" then + format="single" + coverage=prepare_single(list,featuretype) + end + if coverage and next(coverage) then + nofsteps=nofsteps+1 + steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) + end end + checkmerge(specification) + checksteps(specification) + s[i]={ + [stepkey]=steps, + nofsteps=nofsteps, + flags=featureflags, + type=types[featuretype], + } + end + sublookups=s + end + for i=1,#askedsteps do + local list=askedsteps[i] + local coverage=nil + local format=nil + if featuretype=="substitution" then + category="gsub" + coverage=(mapping and list) or prepare_substitution(list,featuretype,nocheck) + elseif featuretype=="ligature" then + category="gsub" + coverage=prepare_ligature(list,featuretype,nocheck) + elseif featuretype=="alternate" then + category="gsub" + coverage=prepare_alternate(list,featuretype,nocheck) + elseif featuretype=="multiple" then + category="gsub" + coverage=prepare_multiple(list,featuretype,nocheck) + elseif featuretype=="kern" or featuretype=="move" then + category="gpos" + format=featuretype + coverage=prepare_kern(list,featuretype) + elseif featuretype=="pair" then + category="gpos" + format="pair" + coverage=prepare_pair(list,featuretype) + elseif featuretype=="single" then + category="gpos" + format="single" + coverage=prepare_single(list,featuretype) + elseif featuretype=="chainsubstitution" then + category="gsub" + coverage=prepare_chain(list,featuretype,sublookups,nocheck) + elseif featuretype=="chainposition" then + category="gpos" + coverage=prepare_chain(list,featuretype,sublookups,nocheck) + else + report_otf("not registering feature %a, unknown category",feature) + return end - checkmerge(specification) - checksteps(specification) - s[i]={ + if coverage and next(coverage) then + nofsteps=nofsteps+1 + steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) + end + end + if nofsteps>0 then + sequence={ + chain=featurechain, + features={ [feature]=askedfeatures }, + flags=featureflags, + name=feature, + order=featureorder, [stepkey]=steps, nofsteps=nofsteps, - flags=featureflags, type=types[featuretype], } end - sublookups=s - end - for i=1,#askedsteps do - local list=askedsteps[i] - local coverage=nil - local format=nil - if featuretype=="substitution" then - category="gsub" - coverage=prepare_substitution(list,featuretype,nocheck) - elseif featuretype=="ligature" then - category="gsub" - coverage=prepare_ligature(list,featuretype,nocheck) - elseif featuretype=="alternate" then - category="gsub" - coverage=prepare_alternate(list,featuretype,nocheck) - elseif featuretype=="multiple" then - category="gsub" - coverage=prepare_multiple(list,featuretype,nocheck) - elseif featuretype=="kern" or featuretype=="move" then - category="gpos" - format=featuretype - coverage=prepare_kern(list,featuretype) - elseif featuretype=="pair" then - category="gpos" - format="pair" - coverage=prepare_pair(list,featuretype) - elseif featuretype=="single" then - category="gpos" - format="single" - coverage=prepare_single(list,featuretype) - elseif featuretype=="chainsubstitution" then - category="gsub" - coverage=prepare_chain(list,featuretype,sublookups) - elseif featuretype=="chainposition" then - category="gpos" - coverage=prepare_chain(list,featuretype,sublookups) - else - report_otf("not registering feature %a, unknown category",feature) - return - end - if coverage and next(coverage) then - nofsteps=nofsteps+1 - steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) - end end - if nofsteps>0 then - for k,v in next,askedfeatures do - if v[1] then - askedfeatures[k]=tohash(v) - end - end - if featureflags[1] then featureflags[1]="mark" end - if featureflags[2] then featureflags[2]="ligature" end - if featureflags[3] then featureflags[3]="base" end - local steptype=types[featuretype] - local sequence={ - chain=featurechain, - features={ [feature]=askedfeatures }, - flags=featureflags, - name=feature, - order=featureorder, - [stepkey]=steps, - nofsteps=nofsteps, - type=steptype, - } + if sequence then checkflags(sequence,resources) checkmerge(sequence) checksteps(sequence) @@ -31055,8 +31587,10 @@ if not modules then modules={} end modules ['font-osd']={ copyright="TAT Zetwerk / PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local insert,imerge,copy,tohash=table.insert,table.imerge,table.copy,table.tohash -local next,type=next,type +local insert,remove,imerge,copy,tohash=table.insert,table.remove,table.imerge,table.copy,table.tohash +local next,type,rawget=next,type,rawget +local formatters=string.formatters +local settings_to_hash=utilities.parsers.settings_to_hash local report=logs.reporter("otf","devanagari") fonts=fonts or {} fonts.analyzers=fonts.analyzers or {} @@ -31066,6 +31600,7 @@ local handlers=otf.handlers local methods=fonts.analyzers.methods local otffeatures=fonts.constructors.features.otf local registerotffeature=otffeatures.register +local trace_steps=false local nuts=nodes.nuts local getnext=nuts.getnext local getprev=nuts.getprev @@ -31091,7 +31626,8 @@ local flushnode=nuts.flushnode local copyinjection=nodes.injections.copy local unsetvalue=attributes.unsetvalue local fontdata=fonts.hashes.identifiers -local a_syllabe=attributes.private('syllabe') +local a_syllabe="syllable" +local a_reordered="reordered" local dotted_circle=0x25CC local c_nbsp=0x00A0 local c_zwnj=0x200C @@ -31111,6 +31647,7 @@ replace_all_nbsp=function(head) return replace_all_nbsp(head) end local processcharacters=nil +local logprocess=nil if context then --removed @@ -31123,6 +31660,8 @@ else end return head end + logprocess=function(str) + end end local indicgroups=characters and characters.indicgroups if not indicgroups and characters then @@ -31232,7 +31771,7 @@ local before_subscript=indicgroups.before_subscript local after_subscript=indicgroups.after_subscript local before_main=indicgroups.before_main local after_main=indicgroups.after_main -local mark_four=table.merged ( +local mark_pre_above_below_post=table.merged ( pre_mark, above_mark, below_mark, @@ -31243,6 +31782,11 @@ local mark_above_below_post=table.merged ( below_mark, post_mark ) +local devanagarihash=table.setmetatableindex(function(t,k) + local v=fontdata[k].resources.devanagari or false + t[k]=v + return v +end) local zw_char={ [c_zwnj]=true, [c_zwj ]=true, @@ -31363,7 +31907,7 @@ local function initializedevanagi(tfmdata) local resources=tfmdata.resources local devanagari=resources.devanagari if not devanagari then - report("adding devanagari features to font") + report("adding features to font") local gsubfeatures=resources.features.gsub local sequences=resources.sequences local sharedfeatures=tfmdata.shared.features @@ -31390,7 +31934,7 @@ local function initializedevanagi(tfmdata) for k,v in next,pre_mark do local locl=coverage[k] if locl then - if #locl>0 then + if #locl>0 then for j=1,#locl do local ck=locl[j] local f=ck[4] @@ -31411,6 +31955,7 @@ local function initializedevanagi(tfmdata) end end end + else end if locl then reorder_matras.steps[1].coverage[locl]=true @@ -31423,7 +31968,7 @@ local function initializedevanagi(tfmdata) if basic_shaping_forms[k] then lastmatch=lastmatch+1 if s~=lastmatch then - table.insert(sequences,lastmatch,table.remove(sequences,s)) + insert(sequences,lastmatch,remove(sequences,s)) end end end @@ -31441,9 +31986,7 @@ local function initializedevanagi(tfmdata) local vatucache={} local pstfcache={} local seqsubset={} - local rephstep={ - coverage={} - } + local rephstep={ coverage={} } local devanagari={ reph=false, vattu=false, @@ -31468,7 +32011,7 @@ local function initializedevanagi(tfmdata) local has_pstf=features.pstf if has_rphf and has_rphf[script] then devanagari.reph=true - elseif (has_blwf and has_blwf[script] ) or (has_vatu and has_vatu[script] ) then + elseif (has_blwf and has_blwf[script]) or (has_vatu and has_vatu[script]) then devanagari.vattu=true for i=1,nofsteps do local step=steps[i] @@ -31476,10 +32019,8 @@ local function initializedevanagi(tfmdata) if coverage then for k,v in next,coverage do for h,w in next,halant do - if v[h] then - if not blwfcache[k] then - blwfcache[k]=v - end + if v[h] and not blwfcache[k] then + blwfcache[k]=v end if has_vatu and has_vatu[script] and not vatucache[k] then vatucache[k]=v @@ -31502,33 +32043,37 @@ local function initializedevanagi(tfmdata) local r=coverage[k] if r then local found=false - if #r>0 then + if #r>0 then for j=1,#r do local ck=r[j] local f=ck[4] local chainlookups=ck[6] - if chainlookups and chainlookups[f] then + if chainlookups then local chainlookup=chainlookups[f] - for j=1,#chainlookup do - local chainstep=chainlookup[j] - local steps=chainstep.steps - local nofsteps=chainstep.nofsteps - for i=1,nofsteps do - local step=steps[i] - local coverage=step.coverage - if coverage then - local h=coverage[k] - if h then - for k,v in next,h do - found=v and v.ligature + if chainlookup then + for j=1,#chainlookup do + local chainstep=chainlookup[j] + local steps=chainstep.steps + local nofsteps=chainstep.nofsteps + for i=1,nofsteps do + local step=steps[i] + local coverage=step.coverage + if coverage then + local h=coverage[k] + if h then + for k,v in next,h do + if v then + found=tonumber(v) or v.ligature + if found then + pre_base_reordering_consonants[found]=true + break + end + end + end if found then - pre_base_reordering_consonants[found]=true break end end - if found then - break - end end end end @@ -31537,10 +32082,12 @@ local function initializedevanagi(tfmdata) end else for k,v in next,r do - found=v and v.ligature - if found then - pre_base_reordering_consonants[found]=true - break + if v then + found=tonumber(v) or v.ligature + if found then + pre_base_reordering_consonants[found]=true + break + end end end end @@ -31558,14 +32105,15 @@ local function initializedevanagi(tfmdata) local step=steps[i] local coverage=step.coverage if coverage then - local reph,rephbase=false,false + local reph=false + local base=false if kind=="rphf" then for k,v in next,ra do local r=coverage[k] if r then - rephbase=k + base=k local h=false - if #r>0 then + if #r>0 then for j=1,#r do local ck=r[j] local f=ck[4] @@ -31585,7 +32133,7 @@ local function initializedevanagi(tfmdata) for k,v in next,halant do local h=r[k] if h then - reph=h.ligature or false + reph=tonumber(h) or h.ligature or false break end end @@ -31602,7 +32150,7 @@ local function initializedevanagi(tfmdata) for k,v in next,halant do local h=r[k] if h then - reph=h.ligature or false + reph=tonumber(h) or h.ligature or false break end end @@ -31613,7 +32161,7 @@ local function initializedevanagi(tfmdata) end end end - seqsubset[#seqsubset+1]={ kind,coverage,reph,rephbase } + seqsubset[#seqsubset+1]={ kind,coverage,reph,base } end end end @@ -31628,7 +32176,7 @@ local function initializedevanagi(tfmdata) local h=coverage[k] if h then local found=false - if #h>0 then + if #h>0 then for j=1,#h do local ck=h[j] local f=ck[4] @@ -31646,10 +32194,12 @@ local function initializedevanagi(tfmdata) local h=coverage[k] if h then for k,v in next,h do - found=v and v.ligature - if found then - pre_base_reordering_consonants[found]=true - break + if v then + found=tonumber(v) or v.ligature + if found then + pre_base_reordering_consonants[found]=true + break + end end end if found then @@ -31663,7 +32213,7 @@ local function initializedevanagi(tfmdata) end else for k,v in next,h do - found=v and v.ligature + found=v and (tonumber(v) or v.ligature) if found then pre_base_reordering_consonants[found]=true break @@ -31689,9 +32239,6 @@ local function initializedevanagi(tfmdata) sharedfeatures["dv03"]=true sharedfeatures["dv04"]=true end - if script=="mlym" or script=="taml" then - devanagari.left_matra_before_base=true - end end end end @@ -31703,6 +32250,44 @@ registerotffeature { node=initializedevanagi, }, } +local function initializeconjuncts(tfmdata,value) + if value then + local resources=tfmdata.resources + local devanagari=resources.devanagari + if devanagari then + local conjuncts="auto" + local movematra="auto" + if type(value)=="string" and value~="auto" then + value=settings_to_hash(value) + conjuncts=rawget(value,"conjuncts") or conjuncts + movematra=rawget(value,"movematra") or movematra + end + if conjuncts=="auto" then + conjuncts="mixed" + end + if movematra=="auto" and + script=="mlym" or + script=="taml" then + movematra="leftbeforebase" + else + movematra="default" + end + devanagari.conjuncts=conjuncts + devanagari.movematra=movematra + if trace_steps then + report("conjuncts %a, movematra %a",conjuncts,movematra) + end + end + end +end +registerotffeature { + name="indic", + description="control indic", + default="auto", + initializers={ + node=initializeconjuncts, + }, +} local show_syntax_errors=false local function inject_syntax_error(head,current,char) local signal=copy_node(current) @@ -31748,36 +32333,40 @@ local function initialize_one(font,attr) end local function contextchain(contexts,n) local char=getchar(n) - for k=1,#contexts do - local ck=contexts[k] - local seq=ck[3] - local f=ck[4] - local l=ck[5] - if (l-f)==1 and seq[f+1][char] then - local ok=true - local c=n - for i=l+1,#seq do - c=getnext(c) - if not c or not seq[i][ischar(c)] then - ok=false - break - end - end - if ok then - c=getprev(n) - for i=1,f-1 do - c=getprev(c) - if not c or not seq[f-i][ischar(c)] then + if not contexts.n then + return contexts[char] + else + for k=1,#contexts do + local ck=contexts[k] + local seq=ck[3] + local f=ck[4] + local l=ck[5] + if (l-f)==1 and seq[f+1][char] then + local ok=true + local c=n + for i=l+1,#seq do + c=getnext(c) + if not c or not seq[i][ischar(c)] then ok=false + break end end - end - if ok then - return true + if ok then + c=getprev(n) + for i=1,f-1 do + c=getprev(c) + if not c or not seq[f-i][ischar(c)] then + ok=false + end + end + end + if ok then + return true + end end end + return false end - return false end local function order_matras(c) local cn=getnext(c) @@ -31806,9 +32395,9 @@ local function order_matras(c) char=getchar(cn) end end +local swapped=table.swapped(states) local function reorder_one(head,start,stop,font,attr,nbspaces) - local reph,vattu,blwfcache,vatucache,pstfcache=initialize_one(font,attr) - local devanagari=fontdata[font].resources.devanagari + local reph,vattu,blwfcache,vatucache,pstfcache=initialize_one(font,attr) local current=start local n=getnext(start) local base=nil @@ -31831,6 +32420,9 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) stop=getprev(stop) head=remove_node(head,current) flushnode(current) + if trace_steps then + logprocess("reorder one, remove nbsp") + end return head,stop,nbspaces else nbspaces=nbspaces+1 @@ -31874,6 +32466,9 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) stop=current end end + if trace_steps then + logprocess("reorder one, handle nbsp") + end end end end @@ -31928,6 +32523,9 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) if lastcons==stop then stop=n end + if trace_steps then + logprocess("reorder one, handle halant") + end end end n=getnext(start) @@ -31952,6 +32550,9 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) if matra==stop then stop=n end + if trace_steps then + logprocess("reorder one, handle matra") + end end local current=start while current~=stop do @@ -31990,14 +32591,19 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) ch=getchar(n) end local tpm=twopart_mark[ch] - while tpm do - local extra=copy_node(n) - copyinjection(extra,n) - ch=tpm[1] - setchar(n,ch) - setchar(extra,tpm[2]) - head=insertnodeafter(head,current,extra) - tpm=twopart_mark[ch] + if tpm then + while tpm do + local extra=copy_node(n) + copyinjection(extra,n) + ch=tpm[1] + setchar(n,ch) + setchar(extra,tpm[2]) + head=insertnodeafter(head,current,extra) + tpm=twopart_mark[ch] + end + if trace_steps then + logprocess("reorder one, handle mark") + end end while c~=stop and dependent_vowel[ch] do c=n @@ -32020,9 +32626,10 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) local bp=getprev(firstcons) local cn=getnext(current) local last=getnext(c) + local done=false while cn~=last do if pre_mark[getchar(cn)] then - if devanagari.left_matra_before_base then + if devanagarihash[font].movematra=="leftbeforebase" then local prev,next=getboth(cn) setlink(prev,next) if cn==stop then @@ -32059,6 +32666,7 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) end cn=next end + done=true elseif current~=base and dependent_vowel[getchar(cn)] then local prev,next=getboth(cn) if next then @@ -32071,6 +32679,7 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) setlink(b,cn,getnext(b)) order_matras(cn) cn=next + done=true elseif current==base and dependent_vowel[getchar(cn)] then local cnn=getnext(cn) order_matras(cn) @@ -32084,9 +32693,14 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) end allreordered=c==stop current=getnext(c) + if done and trace_steps then + logprocess("reorder one, matra") + end end if reph or vattu then - local current,cns=start,nil + local current=start + local cns=nil + local done=false while current~=stop do local c=current local n=getnext(current) @@ -32117,6 +32731,7 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) local next=getnext(b) setlink(c,next) setlink(b,current) + done=true end elseif cns and getnext(cns)~=current then local cp=getprev(current) @@ -32124,6 +32739,7 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) setlink(cp,n) setlink(cns,current) setlink(c,cnsn) + done=true if c==stop then stop=cp break @@ -32163,11 +32779,14 @@ local function reorder_one(head,start,stop,font,attr,nbspaces) end current=getnext(current) end + if done and trace_steps then + logprocess("reorder one, handle reph and vata") + end end if getchar(base)==c_nbsp then nbspaces=nbspaces-1 if base==stop then - stop=getprev(stop) + stop=getprev(stop) end head=remove_node(head,base) flushnode(base) @@ -32195,6 +32814,9 @@ function handlers.devanagari_reorder_matras(head,start) setlink(start,next) setlink(current,start) start=startnext + if trace_steps then + logprocess("reorder matra") + end break end else @@ -32223,6 +32845,9 @@ function handlers.devanagari_reorder_reph(head,start) local char=ischar(current,startfont) if char and getprop(current,a_syllabe)==startattr then if halant[char] then + if trace_steps then + logprocess("reorder reph, handling halant") + end local next=getnext(current) if next then local nextchar=ischar(next,startfont) @@ -32253,6 +32878,9 @@ function handlers.devanagari_reorder_reph(head,start) local char=ischar(current,startfont) if char and getprop(current,a_syllabe)==startattr then if consonant[char] and not getstate(current,s_pref) then + if trace_steps then + logprocess("reorder reph, handling consonant") + end startnext=getnext(start) head=remove_node(head,start) setlink(current,start) @@ -32277,6 +32905,9 @@ function handlers.devanagari_reorder_reph(head,start) local char=ischar(current,startfont) if char and getprop(current,a_syllabe)==startattr then if getstate(current,s_pstf) then + if trace_steps then + logprocess("reorder reph, before postscript, post base") + end startnext=getnext(start) head=remove_node(head,start) setlink(getprev(current),start) @@ -32284,12 +32915,15 @@ function handlers.devanagari_reorder_reph(head,start) start=startnext startattr=getprop(start,a_syllabe) break - elseif not c and (vowel_modifier[char] or stress_tone_mark[char] ) then + elseif not c and (vowel_modifier[char] or stress_tone_mark[char]) then c=current end current=getnext(current) else if c then + if trace_steps then + logprocess("reorder reph, before postscript") + end startnext=getnext(start) head=remove_node(head,start) setlink(getprev(c),start) @@ -32312,7 +32946,13 @@ function handlers.devanagari_reorder_reph(head,start) local state=getstate(current) if before_subscript[rephbase] and (state==s_blwf or state==s_pstf) then c=current + if trace_steps then + logprocess("reorder reph, before subscript") + end elseif after_subscript[rephbase] and (state==s_pstf) then + if trace_steps then + logprocess("reorder reph, after subscript") + end c=current end current=getnext(current) @@ -32343,6 +32983,9 @@ function handlers.devanagari_reorder_reph(head,start) end end if start~=current then + if trace_steps then + logprocess("reorder reph, to end") + end startnext=getnext(start) head=remove_node(head,start) setlink(start,getnext(current)) @@ -32352,9 +32995,8 @@ function handlers.devanagari_reorder_reph(head,start) end return head,start,true end -local reordered_pre_base_reordering_consonants={} function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) - if reordered_pre_base_reordering_consonants[start] then + if getprop(start,a_reordered) then return head,start,true end local current=start @@ -32365,6 +33007,9 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) local next=getnext(current) if char and getprop(current,a_syllabe)==startattr then if halant[char] then + if trace_steps then + logprocess("reorder pre base consonants, handle halant") + end if next then local char=ischar(next,startfont) if char and zw_char[char] and getprop(next,a_syllabe)==startattr then @@ -32376,7 +33021,7 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) head=remove_node(head,start) setlink(start,next) setlink(current,start) - reordered_pre_base_reordering_consonants[start]=true + setprop(start,"reordered",true) start=startnext return head,start,true end @@ -32390,6 +33035,9 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) while current and getprop(current,a_syllabe)==startattr do local char=ischar(current) if (not dependent_vowel[char] and (not getstate(current) or getstate(current,s_init))) then + if trace_steps then + logprocess("reorder pre base consonants, handle vowel or initial") + end startnext=getnext(start) head=remove_node(head,start) if current==head then @@ -32399,7 +33047,7 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) setlink(getprev(current),start) setlink(start,current) end - reordered_pre_base_reordering_consonants[start]=true + setprop(start,"reordered",true) start=startnext break end @@ -32431,6 +33079,9 @@ function handlers.devanagari_remove_joiners(head,start,kind,lookupname,replaceme head=stop end flushlist(start) + if trace_steps then + logprocess("remove joiners") + end return head,stop,true end local function initialize_two(font,attr) @@ -32447,15 +33098,17 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) local basepos=nil local subpos=nil local postpos=nil - reorderreph.coverage={} - rephbase[font]={} + reorderreph.coverage={} + rephbase[font]={} for i=1,#seqsubset do local subset=seqsubset[i] local kind=subset[1] local lookupcache=subset[2] if kind=="rphf" then - reorderreph.coverage[subset[3]]=true - rephbase[font][subset[3]]=subset[4] + local reph=subset[3] + local base=subset[4] + reorderreph.coverage[reph]=true + rephbase[font][reph]=base local current=start local last=getnext(stop) while current~=last do @@ -32464,15 +33117,15 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) local found=lookupcache[c] if found then local next=getnext(current) - if found[getchar(next)] or contextchain(found,next) then + if contextchain(found,next) then local afternext=next~=stop and getnext(next) if afternext and zw_char[getchar(afternext)] then current=afternext elseif current==start then setstate(current,s_rphf) - current=next + current=next else - current=next + current=next end end end @@ -32488,8 +33141,8 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) local found=lookupcache[c] if found then local next=getnext(current) - if found[getchar(next)] or contextchain(found,next) then - if (not getstate(current) and not getstate(next)) then + if contextchain(found,next) then + if not getstate(current) and not getstate(next) then setstate(current,s_pref) setstate(next,s_pref) current=next @@ -32508,10 +33161,10 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) local found=lookupcache[c] if found then local next=getnext(current) - if found[getchar(next)] or contextchain(found,next) then + if contextchain(found,next) then if next~=stop and getchar(getnext(next))==c_zwnj then current=next - elseif (not getstate(current)) then + elseif not getstate(current) then setstate(current,s_half) if not halfpos then halfpos=current @@ -32532,8 +33185,8 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) local found=lookupcache[c] if found then local next=getnext(current) - if found[getchar(next)] or contextchain(found,next) then - if (not getstate(current) and not getstate(next)) then + if contextchain(found,next) then + if not getstate(current) and not getstate(next) then setstate(current,s_blwf) setstate(next,s_blwf) current=next @@ -32553,8 +33206,8 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) local found=lookupcache[c] if found then local next=getnext(current) - if found[getchar(next)] or contextchain(found,next) then - if (not getstate(current) and not getstate(next)) then + if contextchain(found,next) then + if not getstate(current) and not getstate(next) then setstate(current,s_pstf) setstate(next,s_pstf) current=next @@ -32576,6 +33229,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) stop=getprev(stop) head=remove_node(head,current) flushnode(current) + if trace_steps then + logprocess("reorder two, remove nbsp") + end return head,stop,nbspaces else nbspaces=nbspaces+1 @@ -32616,6 +33272,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) end end end + if trace_steps then + logprocess("reorder two, handle nbsp") + end end else local last=getnext(stop) @@ -32645,7 +33304,7 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) return head,stop,nbspaces else if getstate(base) then - setstate(base,unsetvalue) + setstate(base,unsetvalue) end basepos=base end @@ -32666,14 +33325,19 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) local target=nil local cn=getnext(current) local tpm=twopart_mark[char] - while tpm do - local extra=copy_node(current) - copyinjection(extra,current) - char=tpm[1] - setchar(current,char) - setchar(extra,tpm[2]) - head=insertnodeafter(head,current,extra) - tpm=twopart_mark[char] + if tpm then + while tpm do + local extra=copy_node(current) + copyinjection(extra,current) + char=tpm[1] + setchar(current,char) + setchar(extra,tpm[2]) + head=insertnodeafter(head,current,extra) + tpm=twopart_mark[char] + end + if tpm and trace_steps then + logprocess("reorder two, handle matra") + end end if not moved[current] and dependent_vowel[char] then if pre_mark[char] then @@ -32714,6 +33378,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) end setlink(getprev(pos),current) setlink(current,pos) + if trace_steps then + logprocess("reorder two, handle pre mark") + end elseif above_mark[char] then target=basepos if subpos==basepos then @@ -32753,6 +33420,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) end setlink(current,getnext(target)) setlink(target,current) + if trace_steps then + logprocess("reorder two, handle mark") + end end end end @@ -32780,6 +33450,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) stop=prev end cn=next + if trace_steps then + logprocess("reorder two, handle halant and ra") + end end end end @@ -32812,6 +33485,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) setprev(nextnextnext,current) end setlink(nextnext,c) + if trace_steps then + logprocess("reorder two, handle nukta") + end end if stop==current then break end current=getnext(current) @@ -32823,6 +33499,9 @@ local function reorder_two(head,start,stop,font,attr,nbspaces) nbspaces=nbspaces-1 head=remove_node(head,base) flushnode(base) + if trace_steps then + logprocess("reorder two, handle nbsp") + end end return head,stop,nbspaces end @@ -32910,21 +33589,42 @@ local function analyze_next_chars_one(c,font,variant) local already_below_mark local already_post_mark while dependent_vowel[v] do - local vowels=twopart_mark[v] or { v } - for k,v in next,vowels do - if pre_mark[v] and not already_pre_mark then - already_pre_mark=true - elseif above_mark[v] and not already_above_mark then - already_above_mark=true - elseif below_mark[v] and not already_below_mark then - already_below_mark=true - elseif post_mark[v] and not already_post_mark then - already_post_mark=true - else - return c - end - end - c=getnext(c) + local vowels=twopart_mark[v] + if vowels then + for k=1,#vowels do + local v=vowels[k] + if pre_mark[v] and not already_pre_mark then + already_pre_mark=true + elseif above_mark[v] and not already_above_mark then + already_above_mark=true + elseif below_mark[v] and not already_below_mark then + already_below_mark=true + elseif post_mark[v] and not already_post_mark then + already_post_mark=true + elseif devanagarihash[font].conjuncts=="continue" then + else + return c + end + end + else + if pre_mark[v] and not already_pre_mark then + already_pre_mark=true + elseif post_mark[v] and not already_post_mark then + if devanagarihash[font].conjuncts=="mixed" then + return c + else + already_post_mark=true + end + elseif below_mark[v] and not already_below_mark then + already_below_mark=true + elseif above_mark[v] and not already_above_mark then + already_above_mark=true + elseif devanagarihash[font].conjuncts=="continue" then + else + return c + end + end + c=n n=getnext(c) if not n then return c @@ -32935,7 +33635,7 @@ local function analyze_next_chars_one(c,font,variant) end end if nukta[v] then - c=getnext(c) + c=n n=getnext(c) if not n then return c @@ -32946,7 +33646,7 @@ local function analyze_next_chars_one(c,font,variant) end end if halant[v] then - c=getnext(c) + c=n n=getnext(c) if not n then return c @@ -32957,7 +33657,7 @@ local function analyze_next_chars_one(c,font,variant) end end if vowel_modifier[v] then - c=getnext(c) + c=n n=getnext(c) if not n then return c @@ -32968,7 +33668,7 @@ local function analyze_next_chars_one(c,font,variant) end end if stress_tone_mark[v] then - c=getnext(c) + c=n n=getnext(c) if not n then return c @@ -32985,11 +33685,12 @@ local function analyze_next_chars_one(c,font,variant) end end local function analyze_next_chars_two(c,font) - local n=getnext(c) + local n,v + n=getnext(c) if not n then return c end - local v=ischar(n,font) + v=ischar(n,font) if v and nukta[v] then c=n end @@ -33048,11 +33749,11 @@ local function analyze_next_chars_two(c,font) if not c then return end - local n=getnext(c) + n=getnext(c) if not n then return c end - local v=ischar(n,font) + v=ischar(n,font) if not v then return c end @@ -33092,22 +33793,43 @@ local function analyze_next_chars_two(c,font) local already_pre_mark local already_above_mark local already_below_mark - local already_post_mark - while dependent_vowel[v] do - local vowels=twopart_mark[v] or { v } - for k,v in next,vowels do - if pre_mark[v] and not already_pre_mark then - already_pre_mark=true - elseif above_mark[v] and not already_above_mark then - already_above_mark=true - elseif below_mark[v] and not already_below_mark then - already_below_mark=true - elseif post_mark[v] and not already_post_mark then - already_post_mark=true - else - return c - end - end + local already_post_mark + while dependent_vowel[v] do + local vowels=twopart_mark[v] + if vowels then + for k=1,#vowels do + local v=vowels[k] + if pre_mark[v] and not already_pre_mark then + already_pre_mark=true + elseif above_mark[v] and not already_above_mark then + already_above_mark=true + elseif below_mark[v] and not already_below_mark then + already_below_mark=true + elseif post_mark[v] and not already_post_mark then + already_post_mark=true + elseif devanagarihash[font].conjuncts=="continue" then + else + return c + end + end + else + if pre_mark[v] and not already_pre_mark then + already_pre_mark=true + elseif post_mark[v] and not already_post_mark then + if devanagarihash[font].conjuncts=="mixed" then + return c + else + already_post_mark=true + end + elseif below_mark[v] and not already_below_mark then + already_below_mark=true + elseif above_mark[v] and not already_above_mark then + already_above_mark=true + elseif devanagarihash[font].conjuncts=="continue" then + else + return c + end + end c=n n=getnext(c) if not n then @@ -33342,7 +34064,7 @@ local function method_one(head,font,attr) end else if show_syntax_errors then - local mark=mark_four[char] + local mark=mark_pre_above_below_post[char] if mark then head,current=inject_syntax_error(head,current,char) end @@ -33363,15 +34085,15 @@ local function method_one(head,font,attr) while current do local char=ischar(current,font) if char then - if n==0 and not getstate(current) then - setstate(current,s_init) - end - n=n+1 - else - n=0 - end - current=getnext(current) - end + if n==0 and not getstate(current) then + setstate(current,s_init) + end + n=n+1 + else + n=0 + end + current=getnext(current) + end return head,done end local function method_two(head,font,attr) @@ -33441,7 +34163,7 @@ local function method_two(head,font,attr) if not syllableend and show_syntax_errors then local char=ischar(current,font) if char and not getstate(current) then - local mark=mark_four[char] + local mark=mark_pre_above_below_post[char] if mark then head,current=inject_syntax_error(head,current,char) end @@ -33458,15 +34180,15 @@ local function method_two(head,font,attr) while current do local char=ischar(current,font) if char then - if n==0 and not getstate(current) then - setstate(current,s_init) - end - n=n+1 - else - n=0 - end - current=getnext(current) - end + if n==0 and not getstate(current) then + setstate(current,s_init) + end + n=n+1 + else + n=0 + end + current=getnext(current) + end return head,done end for i=1,nofscripts do @@ -36161,7 +36883,7 @@ local afm=fonts.handlers.afm local pfb=fonts.handlers.pfb local hashes=fonts.hashes local identifiers=hashes.identifiers -local version=0.010 +local version=otf.version or 0.011 local shapescache=containers.define("fonts","shapes",version,true) local streamscache=containers.define("fonts","streams",version,true) local compact_streams=false @@ -36469,7 +37191,7 @@ local function getstreamhash(fontid) local fontdata=identifiers[fontid] if fontdata then local properties=fontdata.properties - return makehash(properties.filename,properties.subfont,properties.instance) + return makehash(properties.filename,properties.subfont,properties.instance),fontdata end end local function loadstreamdata(fontdata) diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-fil.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-fil.lua index 183f7bea81f..0b20264ecc9 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-fil.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-fil.lua @@ -203,23 +203,23 @@ function files.readinteger4le(f) end function files.readfixed2(f) - local a, b = byte(f:read(2),1,2) - if a >= 0x80 then - tonumber((a - 0x100) .. "." .. b) - else - tonumber(( a ) .. "." .. b) + local n1, n2 = byte(f:read(2),1,2) + if n1 >= 0x80 then + n1 = n1 - 0x100 end + return n1 + n2/0xFF end -- (real) (n>>16) + ((n&0xffff)/65536.0)) but no cast in lua (we could use unpack) function files.readfixed4(f) local a, b, c, d = byte(f:read(4),1,4) - if a >= 0x80 then - tonumber((0x100 * a + b - 0x10000) .. "." .. (0x100 * c + d)) - else - tonumber((0x100 * a + b ) .. "." .. (0x100 * c + d)) + local n1 = 0x100 * a + b + local n2 = 0x100 * c + d + if n1 >= 0x8000 then + n1 = n1 - 0x10000 end + return n1 + n2/0xFFFF end -- (real) ((n<<16)>>(16+14)) + ((n&0x3fff)/16384.0)) diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-str.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-str.lua index 0d1f39de9d7..b5c721a41d2 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-str.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-str.lua @@ -10,12 +10,13 @@ utilities = utilities or { } utilities.strings = utilities.strings or { } local strings = utilities.strings -local format, gsub, rep, sub, find = string.format, string.gsub, string.rep, string.sub, string.find +local format, gsub, rep, sub, find, char = string.format, string.gsub, string.rep, string.sub, string.find, string.char local load, dump = load, string.dump local tonumber, type, tostring, next, setmetatable = tonumber, type, tostring, next, setmetatable local unpack, concat = table.unpack, table.concat local P, V, C, S, R, Ct, Cs, Cp, Carg, Cc = lpeg.P, lpeg.V, lpeg.C, lpeg.S, lpeg.R, lpeg.Ct, lpeg.Cs, lpeg.Cp, lpeg.Carg, lpeg.Cc local patterns, lpegmatch = lpeg.patterns, lpeg.match +local tsplitat = lpeg.tsplitat local utfchar, utfbyte, utflen = utf.char, utf.byte, utf.len ----- loadstripped = utilities.lua.loadstripped @@ -622,7 +623,7 @@ local template = [[ return function(%s) return %s end ]] --- this might move +-- We only use fast serialize in controlled cases. local pattern = Cs(Cc('"') * ( (1-S('"\\\n\r'))^1 @@ -632,12 +633,43 @@ local pattern = Cs(Cc('"') * ( + P('\r') / '\\r' )^0 * Cc('"')) +-- -- I need to do more experiments with this: +-- +-- local pattern = Cs(Cc('"') * ( +-- (1-S('"\\\n\r'))^1 +-- + P('"') / '\\034' +-- + P('\\') / '\\092' +-- + P('\n') / '\\013' +-- + P('\r') / '\\010' +-- )^0 * Cc('"')) + patterns.escapedquotes = pattern function string.escapedquotes(s) return lpegmatch(pattern,s) end +local pattern = (1 - P("\\"))^1 ; pattern = Cs ( + pattern + * ( (P("\\") / "" * (digit^-3 / function(s) return char(tonumber(s)) end)) + pattern )^1 +) + +patterns.unescapedquotes = pattern + +function string.unescapedquotes(s) + return lpegmatch(pattern,s) or s +end + +-- function string.longifneeded(s) +-- if find(s,'["\\\n\r]') then +-- return "[===[" .. s .. "]===]" +-- else +-- return '"' .. s ..'"' +-- end +-- end + +string.texnewlines = lpeg.replacer(patterns.newline,"\r",true) + -- print(string.escapedquotes('1\\23\n"')) -- but for now here @@ -1476,7 +1508,7 @@ end if not string.explode then - local tsplitat = lpeg.tsplitat + -- local tsplitat = lpeg.tsplitat local p_utf = patterns.utf8character local p_check = C(p_utf) * (P("+") * Cc(true))^0 @@ -1499,3 +1531,24 @@ if not string.explode then end end + + +do + + local p_whitespace = patterns.whitespace^1 + + local cache = setmetatable({ }, { __index = function(t,k) + local p = tsplitat(p_whitespace * P(k) * p_whitespace) + local v = function(s) + return lpegmatch(p,s) + end + t[k] = v + return v + end }) + + function string.wordsplitter(s) + return cache[s] + end + +end + diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua index 9c2d204aaab..86aff98fb4f 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua @@ -6,8 +6,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-auxiliary", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / auxiliary functions", license = "GPL v2.0" } diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-colors.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-colors.lua index c6a437c92a3..e0bd759de58 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-colors.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-colors.lua @@ -5,8 +5,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-colors", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / color", license = "GPL v2.0", author = "Khaled Hosny, Elie Roux, Philipp Gesang, Dohyun Kim, David Carlisle", diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-configuration.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-configuration.lua index 76a8d687546..50618d72af1 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-configuration.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-configuration.lua @@ -7,8 +7,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-configuration", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / config file reader", license = "GPL v2.0" } @@ -218,6 +218,7 @@ local default_config = { version = luaotfload.version, statistics = false, termwidth = nil, + keepnames = true, }, paths = { names_dir = "names", @@ -611,6 +612,7 @@ local option_spec = { return w end, }, + keepnames = { in_t = boolean_t, }, }, paths = { names_dir = { in_t = string_t, }, @@ -741,7 +743,7 @@ local formatters = { anon_sequence = { false, format_list }, color_callback = { false, format_string }, definer = { false, format_string }, - fontloader = { false, format_string }, + fontloader = { true, format_string }, log_level = { false, format_integer }, resolver = { false, format_string }, }, diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua index b1d264f9b78..8f0ef41fc73 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua @@ -5,8 +5,8 @@ do -- block to avoid to many local variables error assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-database", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / database", license = "GPL v2.0", author = "Khaled Hosny, Elie Roux, Philipp Gesang, Marcel Krüger", @@ -142,8 +142,7 @@ do end local otfhandler = fonts.handlers.otf or { } -local gzipload = gzip.load -local gzipsave = gzip.save +local gzipopen = gzip.open local iolines = io.lines local ioopen = io.open local kpseexpand_path = kpse.expand_path @@ -159,6 +158,7 @@ local mathmin = math.min local osgetenv = os.getenv local osgettimeofday = os.gettimeofday local osremove = os.remove +local dump = string.dump local stringfind = string.find local stringformat = string.format local stringgmatch = string.gmatch @@ -486,42 +486,47 @@ local function initialize_namedata (formats, created) } end +-- A helper to load a file which might be gziped +local function load_maybe_gzip (path, binary) + local gzippath = path .. '.gz' + local f = ioopen (gzippath, 'rb') + if f then + path = gzippath + local result = f:read'*a' + result = gzip.decompress(result, 31) + f:close() + return path, result + else + f, msg = ioopen (path, binary and 'rb' or 'r') + if f then + local result = f:read'*a' + f:close() + return path, result + end + end + + return nil, msg +end + --- When loading a lua file we try its binary complement first, which --- is assumed to be located at an identical path, carrying the suffix --- .luc. ---- string -> (string * table) -local function load_lua_file (path) - local foundname = filereplacesuffix (path, "luc") - local code = nil - - local fh = ioopen (foundname, "rb") -- try bin first - if fh then - local chunk = fh:read"*all" - fh:close() - code = load (chunk, "b") - end - - if not code then --- fall back to text file - foundname = filereplacesuffix (path, "lua") - fh = ioopen(foundname, "rb") - if fh then - local chunk = fh:read"*all" - fh:close() - code = load (chunk, "t") - end - end - - if not code then --- probe gzipped file - foundname = filereplacesuffix (path, "lua.gz") - local chunk = gzipload (foundname) - if chunk then - code = load (chunk, "t") +--- string -> string -> (string * table) +local function load_lua_file (path_lua, path_luc) + local foundname, chunk = load_maybe_gzip (path_luc, true) + if foundname then + chunk = assert (load (chunk, 'b')) + else + foundname, chunk = load_maybe_gzip (path_lua, false) + if foundname then + chunk = assert (load (chunk, 't')) + else + return nil end end - if not code then return nil, nil end - return foundname, code () + return foundname, chunk() end --- define locals in scope @@ -543,7 +548,8 @@ local fuzzy_limit = 1 --- display closest only --- bool? -> -> bool? -> dbobj option local function load_names (dry_run, no_rebuild) local starttime = osgettimeofday () - local foundname, data = load_lua_file (config.luaotfload.paths.index_path_lua) + local foundname, data = load_lua_file (config.luaotfload.paths.index_path_lua, + config.luaotfload.paths.index_path_luc) if data then logreport ("log", 0, "db", @@ -624,7 +630,8 @@ end --- unit -> unit local function load_lookups ( ) - local foundname, data = load_lua_file(config.luaotfload.paths.lookup_path_lua) + local foundname, data = load_lua_file(config.luaotfload.paths.lookup_path_lua, + config.luaotfload.paths.lookup_path_luc) if data then logreport ("log", 0, "cache", "Lookup cache loaded from %s.", foundname) logreport ("term", 3, "cache", @@ -638,11 +645,12 @@ local function load_lookups ( ) end local regular_synonym = { - book = true, - normal = true, - plain = true, - regular = true, - roman = true, + book = 0, + normal = 0, + plain = 0, + regular = 0, + roman = 0, + medium = 1000, -- Some font authors haven't got the memo that medium is not regular } local style_synonym = { @@ -1577,8 +1585,6 @@ local function organize_namedata (rawinfo, or nametable.family or rawinfo.familyname or info.familyname --- local default_modifier = nametable.typographicsubfamily --- or nametable.subfamily local fontnames = { --- see --- https://developer.apple.com/fonts/TTRefMan/RM06/Chap6name.html @@ -1642,7 +1648,7 @@ local function split_fontname (fontname) end end -local function organize_styledata (metadata, rawinfo, info) +local function organize_styledata (metadata, rawinfo) local pfminfo = metadata.pfminfo local names = rawinfo.names return { @@ -1686,8 +1692,7 @@ local function ot_fullinfo (filename, basename, info) local style = organize_styledata (metadata, - rawinfo, - info) + rawinfo) local res = { file = { base = basename, full = filename, @@ -1929,7 +1934,7 @@ local function read_font_names (fullname, if insert_fullinfo (fullname, basename, n_font, loader, format, location, targetmappings, targetentrystatus, - info) + info[n_font]) then success = true end @@ -2368,7 +2373,7 @@ local function collect_font_filenames_texmf () fontdirs = fontdirs .. path_separator .. expanded_path "type1 fonts" fontdirs = fontdirs .. path_separator .. expanded_path "afm" - fontdirs = filesplitpath (fontdirs) or { } + fontdirs = filesplitpath (fontdirs) or { } local tasks = filter_out_pwd (fontdirs) logreport ("both", 3, "db", @@ -2725,95 +2730,51 @@ local function generate_filedata (mappings) return files end -local bold_spectrum_low = 501 --- 500 is medium, 900 heavy/black -local normal_weight = 400 -local bold_weight = 700 -local normal_width = 5 - -local pick_style -local pick_fallback_style -local check_regular - -do - function pick_style (typographicsubfamily, subfamily) - return style_synonym [typographicsubfamily or subfamily or ""] - end - - function pick_fallback_style (italicangle, pfmweight, width) - --[[-- - More aggressive, but only to determine bold faces. - Note: Before you make this test more inclusive, ensure - no fonts are matched in the bold synonym spectrum over - a literally “bold[italic]” one. In the past, heuristics - been tried but ultimately caused unwanted modifiers - polluting the lookup table. What doesn’t work is, e. g. - treating weights > 500 as bold or allowing synonyms like - “heavy”, “black”. - --]]-- - if width == normal_width then - if pfmweight == bold_weight then - --- bold spectrum matches - if italicangle == 0 then - return "b" - end - return "bi" - elseif pfmweight == normal_weight then - if italicangle ~= 0 then - return "i" - end - end - return tostring(pfmweight) .. (italicangle == 0 and "" or "i") - end - return false - end - - --- we use only exact matches here since there are constructs - --- like “regularitalic” (Cabin, Bodoni Old Fashion) - - function check_regular (typographicsubfamily, - subfamily, - italicangle, - weight, - width, - pfmweight) - local plausible_weight = false - --[[-- - This filters out undesirable candidates that specify their - typographicsubfamily or subfamily as “regular” but are actually of - “semibold” or other weight—another drawback of the - oversimplifying classification into only three styles (r, i, - b, bi). - --]]-- - if italicangle == 0 then - if pfmweight == 400 then - --[[-- - Some fonts like Dejavu advertise an undistinguished - regular and a “condensed” version with the same - weight whilst also providing the style info in the - typographic subfamily instead of the subfamily (i. e. - the converse of what Adobe’s doing). The only way to - weed out the undesired pseudo-regular shape is to - peek at its advertised width (4 vs. 5). - --]]-- - if width then - plausible_weight = width == normal_width - else - plausible_weight = true - end - elseif weight and regular_synonym [weight] then - plausible_weight = true - end - end - - if plausible_weight then - if subfamily then - if regular_synonym [subfamily] then return "r" end - elseif typographicsubfamily then - if regular_synonym [typographicsubfamily] then return "r" end - end - end - return false - end +local function regular_score(entry) + return 10000 * (entry.italicangle or 0)^2 -- We really don't want italic fonts here (italic font have an angle around 10) + + .01 * ((entry.pfmweight or 400) - 400)^2 -- weights are normally multiples of 100, so they are still quite large after .01 + + ((entry.width or 5) - 5)^2 + + (regular_synonym[entry.subfamily or entry.typographicsubfamily] or 1000000) + + (entry.pfmweight > 500 and 1000 or 0) +end +local function italic_score(entry, regular_entry) + local regularangle = regular_entry.italicangle or 0 + local angle = entry.italicangle or 0 + if (angle == 0 or angle == regularangle) + and (entry.subfamily == regular_entry.subfamily or style_synonym[entry.subfamily] ~= 'i') then + return -- This font is not italic in any way + end + return .1 * (angle - regularangle - 10)^2 -- Should there ever be multiple levels of italicness... + + 0.1 * ((entry.pfmweight or 400) - (regular_entry.pfmweight or 400))^2 -- weights are normally multiples of 100, so they are still quite large after .01 + + ((entry.width or 5) - regular_entry.width)^2 + + (style_synonym[entry.subfamily or entry.typographicsubfamily] == 'i' and 0 or 1000000) +end +local function bold_score(entry, regular_entry) + local regularweight = regular_entry.pfmweight or 400 + local weight = entry.pfmweight + if weight < regularweight + 100 + and (entry.subfamily == regular_entry.subfamily or style_synonym[entry.subfamily] ~= 'b') then + return -- This font is not bold in any way + end + return 10000 * (entry.italicangle or 0)^2 -- We really don't want italic fonts here (italic font have an angle around 10) + + .01 * ((entry.pfmweight or 400) - (regularweight + 200))^2 -- weights are normally multiples of 100, so they are still quite large after .01 + + ((entry.width or 5) - regular_entry.width)^2 + + (style_synonym[entry.subfamily or entry.typographicsubfamily] == 'b' and 0 or 1000000) + + (entry.pfmweight > 500 and 0 or 10000) +end +local function bolditalic_score(entry, bold_entry, italic_entry) + local italicangle = italic_entry.italicangle or 0 + local angle = entry.italicangle or 0 + local boldweight = bold_entry.pfmweight or 400 + local weight = entry.pfmweight or 400 + if (angle == 0 or weight < boldweight) + and (entry.subfamily == bold_entry.subfamily or entry.subfamily == italic_entry.subfamily or style_synonym[entry.subfamily] ~= 'bi') then + return -- This font is not italic in any way + end + return 100 * (angle - italicangle)^2 + + (weight - boldweight)^2 + + ((entry.width or 5) - bold_entry.width)^2 + + (style_synonym[entry.subfamily or entry.typographicsubfamily] == 'bi' and 0 or 1000000) end local function pull_values (entry) @@ -2854,7 +2815,7 @@ local function pull_values (entry) end end -local function add_family (name, subtable, modifier, entry) +local function add_family (name, subtable, entry) if not name then --- probably borked font return end @@ -2866,22 +2827,9 @@ local function add_family (name, subtable, modifier, entry) familytable [#familytable + 1] = { index = entry.index, - modifier = modifier, } end -local function add_lastresort_regular (name, subtable, entry) - if not name then --- probably borked font - return - end - local familytable = subtable [name] - if not familytable then - familytable = { } - subtable [name] = familytable - end - familytable.fallback = entry.index -end - local function get_subtable (families, entry) local location = entry.location local format = entry.format @@ -2919,27 +2867,7 @@ local function collect_families (mappings) local width = entry.width local pfmweight = entry.pfmweight local italicangle = entry.italicangle - local modifier = pick_style (typographicsubfamily, subfamily) - - if not modifier then --- regular, exact only - modifier = check_regular (typographicsubfamily, - subfamily, - italicangle, - weight, - width, - pfmweight) - end - - if not modifier then - modifier = pick_fallback_style (italicangle, pfmweight, width) - end - - if modifier then - add_family (familyname, subtable, modifier, entry) - end - if modifier ~= 'r' and regular_synonym[typographicsubfamily or subfamily or ''] then - add_lastresort_regular (familyname, subtable, entry) - end + add_family (familyname, subtable, entry) end collectgarbage "collect" @@ -2983,99 +2911,135 @@ end local style_categories = { "r", "b", "i", "bi" } local bold_categories = { "b", "bi" } +local huge = math.huge local function group_modifiers (mappings, families) logreport ("info", 2, "db", "Analyzing shapes, weights, and styles.") for location, location_data in next, families do for format, format_data in next, location_data do for familyname, collected in next, format_data do - local styledata = { } --- will replace the “collected” table - local lastresort_regular = collected.fallback - collected.fallback = nil - --- First, fill in the ordinary style data that - --- fits neatly into the four relevant modifier - --- categories. - for _, modifier in next, style_categories do - local entries - for key, info in next, collected do - if info.modifier == modifier then - if not entries then - entries = { } - end - local index = info.index - local entry = mappings [index] - local size = entry.size + local best_score = huge + local best_match + for i=1,#collected do + local v = collected[i] + local entry = mappings[v.index] + local score = regular_score(entry) + if score <= best_score then + v.prev = best_score == score and best_match or nil + best_score = score + best_match = v + end + end + local regular = {} + repeat + local index = best_match.index + local entry = mappings[index] + local size = entry.size + if size then + regular [#regular + 1] = { + size [1], + size [2], + size [3], + index, + } + else + regular.default = index + end + best_match = best_match.prev + until not best_match + local regular_entry = mappings[regular.default or regular[1][4]] + local best_match_i, best_match_b + local best_score_i, best_score_b = 10000000000, 10000000000 + for i=1,#collected do + local v = collected[i] + local entry = mappings[v.index] + local score_i = italic_score(entry, regular_entry) + local score_b = bold_score(entry, regular_entry) + if score_i and score_i <= best_score_i then + v.prev = best_score_i == score_i and best_match_i or nil + best_score_i = score_i + best_match_i = v + end + if score_b and score_b <= best_score_b then + v.prev = best_score_b == score_b and best_match_b or nil + best_score_b = score_b + best_match_b = v + end + end + local italic, bold + if best_match_i then + italic = {} + repeat + local index = best_match_i.index + local entry = mappings[index] + local size = entry.size + if size then + italic [#italic + 1] = { + size [1], + size [2], + size [3], + index, + } + else + italic.default = index + end + best_match_i = best_match_i.prev + until not best_match_i + end + if best_match_b then + bold = {} + repeat + local index = best_match_b.index + local entry = mappings[index] + local size = entry.size + if size then + bold [#bold + 1] = { + size [1], + size [2], + size [3], + index, + } + else + bold.default = index + end + best_match_b = best_match_b.prev + until not best_match_b + end + local bolditalic + if bold and italic then + best_score = 1000000000000 + local bold_entry = mappings[bold.default or bold[1][4]] + local italic_entry = mappings[italic.default or italic[1][4]] + for i=1,#collected do + local v = collected[i] + local entry = mappings[v.index] + local score = bolditalic_score(entry, bold_entry, italic_entry) + if score and score <= best_score then + v.prev = best_score == score and best_match or nil + best_score = score + best_match = v + end + end + if best_match then + bolditalic = {} + repeat + local index = best_match.index + local entry = mappings[index] + local size = entry.size if size then - entries [#entries + 1] = { + bolditalic [#bolditalic + 1] = { size [1], size [2], size [3], index, } else - entries.default = index + bolditalic.default = index end - collected [key] = nil - end - styledata [modifier] = entries + best_match = best_match.prev + until not best_match end end - if not styledata.r and lastresort_regular then - styledata.r = {default = lastresort_regular} - end - - --- At this point the family set may still lack - --- entries for bold or bold italic. We will fill - --- those in using the modifier with the numeric - --- weight that is closest to bold (700). - if next (collected) then --- there are uncategorized entries - for _, modifier in next, bold_categories do - if not styledata [modifier] then - local closest - local minimum = 2^51 - for key, info in next, collected do - local info_modifier = tonumber (info.modifier) and "b" or "bi" - if modifier == info_modifier then - local index = info.index - local entry = mappings [index] - local weight = entry.pfmweight - local diff = weight < 700 and 700 - weight or weight - 700 - if weight > 500 and diff < minimum then - minimum = diff - closest = weight - end - end - end - if closest then - --- We know there is a substitute face for the modifier. - --- Now we scan the list again to extract the size data - --- in case the shape is available at multiple sizes. - local entries = { } - for key, info in next, collected do - local info_modifier = tonumber (info.modifier) and "b" or "bi" - if modifier == info_modifier then - local index = info.index - local entry = mappings [index] - local size = entry.size - if entry.pfmweight == closest then - if size then - entries [#entries + 1] = { - size [1], - size [2], - size [3], - index, - } - else - entries.default = index - end - end - end - end - styledata [modifier] = entries - end - end - end - end - format_data [familyname] = styledata + format_data [familyname] = { r = regular, b = bold, i = italic, bi = bolditalic } end end end @@ -3514,28 +3478,62 @@ function update_names (currentnames, force, dry_run) return targetnames end +local function compress_dummy(data) return data end +--- string -> string -> (string * table) +local function save_lua_table (data, path_lua, path_luc, compress) + if compress then + osremove(path_lua) + osremove(path_luc) + path_lua, path_luc = path_lua .. '.gz', path_luc .. '.gz' + else + osremove(path_lua .. '.gz') + osremove(path_luc .. '.gz') + end + local file_lua, msg = ioopen(path_lua, compress and 'wb' or 'w') + if not file_lua then + logreport ("info", 0, "cache", "Failed to write %q: %s", path_lua, msg) + end + local file_luc file_luc, msg = ioopen(path_luc, 'wb') + if not file_luc then + logreport ("info", 0, "cache", "Failed to write %q: %s", path_luc, msg) + end + if not (file_lua or file_luc) then + return + end + -- If we can only write one of the files, try to remove the other + -- one to avoid them beiing inconsistent. This will probably fail, + -- but in some situations we might be allowed to delete a file we + -- can't write to. + if not file_lua then + osremove(path_lua) + elseif not file_luc then + osremove(path_luc) + end + local serialized = tableserialize (data, true) + if file_lua then + file_lua:write(compress and gzip.compress(serialized, 31) or serialized) + file_lua:close() + end + if file_luc then + local compiled = dump(assert(load(serialized, 't')), true) + file_luc:write(compress and gzip.compress(compiled, 31) or compiled) + file_luc:close() + end + -- Even if we could write one file but not the other one it's still an + -- error since reloading is then unreliable. + return file_lua and path_lua or nil, file_luc and path_luc or nil +end + --- unit -> bool function save_lookups ( ) local paths = config.luaotfload.paths local luaname, lucname = paths.lookup_path_lua, paths.lookup_path_luc - if fileiswritable (luaname) and fileiswritable (lucname) then - tabletofile (luaname, lookup_cache, true) - osremove (lucname) - caches.compile (lookup_cache, luaname, lucname) - --- double check ... - if lfsisfile (luaname) and lfsisfile (lucname) then - logreport ("both", 3, "cache", "Lookup cache saved.") - return true - end - logreport ("info", 0, "cache", "Could not compile lookup cache.") - return false - end - logreport ("info", 0, "cache", "Lookup cache file not writable.") - if not fileiswritable (luaname) then - logreport ("info", 0, "cache", "Failed to write %s.", luaname) - end - if not fileiswritable (lucname) then - logreport ("info", 0, "cache", "Failed to write %s.", lucname) + luaname, lucname = save_lua_table(lookup_cache, luaname, lucname) + if luaname and lucname then + logreport ("both", 3, "cache", "Lookup cache saved.") + return true + else + logreport ("info", 0, "cache", "Lookup cache file not writable.") end return false end @@ -3553,45 +3551,15 @@ function save_names (currentnames) end local paths = config.luaotfload.paths local luaname, lucname = paths.index_path_lua, paths.index_path_luc - if fileiswritable (luaname) and fileiswritable (lucname) then - osremove (lucname) - local gzname = luaname .. ".gz" - if config.luaotfload.db.compress then - local serialized = tableserialize (currentnames, true) - gzipsave (gzname, serialized) - caches.compile (currentnames, "", lucname) - else - tabletofile (luaname, currentnames, true) - caches.compile (currentnames, luaname, lucname) - end + local compress = config.luaotfload.db.compress + luaname, lucname = save_lua_table(currentnames, luaname, lucname, compress) + if luaname and lucname then logreport ("info", 2, "db", "Font index saved at ...") - local success = false - if lfsisfile (luaname) then - logreport ("info", 2, "db", "Text: " .. luaname) - success = true - end - if lfsisfile (gzname) then - logreport ("info", 2, "db", "Gzip: " .. gzname) - success = true - end - if lfsisfile (lucname) then - logreport ("info", 2, "db", "Byte: " .. lucname) - success = true - end - if success then - return true - else - logreport ("info", 0, "db", "Could not compile font index.") - return false - end + logreport ("info", 2, "db", "Text: " .. luaname) + logreport ("info", 2, "db", "Byte: " .. lucname) + return true end logreport ("info", 0, "db", "Index file not writable") - if not fileiswritable (luaname) then - logreport ("info", 0, "db", "Failed to write %s.", luaname) - end - if not fileiswritable (lucname) then - logreport ("info", 0, "db", "Failed to write %s.", lucname) - end return false end @@ -3829,10 +3797,7 @@ return function () fonts.definers = fonts.definers or { resolvers = { } } names.blacklist = blacklist - -- MK Changed to rebuild with case insensitive fallback. - -- Negative version to indicate generation by modified code. - names.version = -2 --- decrease monotonically - -- /MK + names.version = 6 --- increase monotonically names.data = nil --- contains the loaded database names.lookups = nil --- contains the lookup cache diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-diagnostics.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-diagnostics.lua index fe959a50dea..622714d14b1 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-diagnostics.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-diagnostics.lua @@ -7,8 +7,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-diagnostics", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload-tool submodule / diagnostics", license = "GPL v2.0" } diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-dvi.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-dvi.lua index f368c3ae5a7..2f8abefa937 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-dvi.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-dvi.lua @@ -6,8 +6,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-dvi", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / DVI", license = "GPL v2.0", author = "Marcel Krüger", diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-embolden.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-embolden.lua index ca035bc4cd0..d0a2e60555e 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-embolden.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-embolden.lua @@ -5,8 +5,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-embolden", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / embolden", license = "GPL v2.0", author = "Marcel Krüger" diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-fallback.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-fallback.lua index 3e5d65eacaa..a3f28e77a4a 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-fallback.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-fallback.lua @@ -5,8 +5,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-fallback", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / fallback", license = "GPL v2.0", author = "Marcel Krüger" diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua index bedcd59d764..eecf1aa0f59 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua @@ -5,8 +5,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-features", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / features", license = "GPL v2.0", author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang, Marcel Krüger", @@ -30,6 +30,8 @@ local table = table local tabletohash = table.tohash local tablesort = table.sort +local stringunpack = string.unpack + --- this appears to be based in part on luatex-fonts-def.lua local fonts = fonts @@ -322,6 +324,7 @@ local supported = { b = "b", i = "i", bi = "bi", + r = "r", aat = false, icu = false, gr = false, @@ -777,6 +780,100 @@ do } end +do + local function restore(tfmdata, value, features) + if not tfmdata.properties.monospaced then return end + if features.fixedspace then return end -- In this case, 'auto' is true + local parameters = tfmdata.parameters + local space = parameters.space + parameters.space_stretch, parameters.space_shrink = space/2, space/3 + end + fonts.constructors.features.otf.register { + name = 'internal__variablespace', + default = true, + initializers = { + base = restore, + node = restore, + }, + } + local function node_fixedspace(tfmdata, value, features) + if value == 'auto' then return end + if tfmdata.properties.monospaced then return end -- handled by internal__variablespace + local parameters = tfmdata.parameters + parameters.space_stretch, parameters.space_shrink = 0, 0 + end + local hb = luaotfload.harfbuzz + local post_tag = hb and hb.Tag.new'post' + local function harf_fixedspace(tfmdata, value, features) + if value == 'auto' then + -- We have to determine if we have a monospace font. + -- Let's be honest, it would be boring if that were easy. + local post_table = tfmdata.hb.shared.face:get_table(post_tag):get_data() + if #post_table < 16 then + -- Invalid OpenType font... Let's assume that it's not + -- monospaced: + return + end + local monospaced = string.unpack('>I4', post_table, 13) ~= 0 + if not monospaced then return end -- FIXME: How to determine? + end + local parameters = tfmdata.parameters + parameters.space_stretch, parameters.space_shrink = 0, 0 + end + fonts.constructors.features.otf.register { + name = 'fixedspace', + description = 'Do not stretch or shrink spaces', + default = 'auto', + initializers = { + base = node_fixedspace, + node = node_fixedspace, + plug = harf_fixedspace, + }, + } +end + +local uni_normalize = require'lua-uni-normalize'.direct +local normalize_lookup = setmetatable({}, {__index = function(t, f) + local fontdir = assert(font.getfont(f)) + local normalize_func = t[fontdir] + local characters = fontdir.characters + local function result(head) + return normalize_func(head, f, characters, true) + end + t[fontdir] = result + return result +end}) +local normalize_funcs = { + nfc = uni_normalize.NFC, + nfd = uni_normalize.NFD, + nfkd = uni_normalize.NFKD, +} +fonts.constructors.features.otf.register { + name = 'normalize', + default = 'nfc', + description = 'Normalize text to NFC before shaping', + manipulators = { + node = function(fonttable, _, value) + if value == true then + value = 'nfc' + end + local func = normalize_funcs[value] + if not func then + report ("report", 0, "features", + "Unsupported normalization method replaced by NFC") + func = normalize_funcs.nfc + end + normalize_lookup[fonttable] = func + end, + }, + processors = { + node = function(head, f, _, _, _) + return normalize_lookup[f](head) + end, + }, +} + + return function () if not fonts and fonts.handlers then report ("log", 0, "features", diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-filelist.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-filelist.lua index e3fc38acd3c..abd6458db97 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-filelist.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-filelist.lua @@ -6,8 +6,8 @@ if luaotfload_module then luaotfload_module { name = "luaotfload-filelist", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / filelist", license = "GPL v2.0" } end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-glyphlist.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-glyphlist.lua index 93a2eaeffd0..ce3cc314b40 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-glyphlist.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-glyphlist.lua @@ -1,8 +1,8 @@ if luaotfload_module then luaotfload_module { name = "luaotfload-glyphlist", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / autogenerated glyphlist", copyright = "derived from https://raw.githubusercontent.com/adobe-type-tools/agl-aglfn/master/glyphlist.txt", original = "Adobe Glyph List, version 2.0, September 20, 2002", diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-define.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-define.lua index f128adbaaea..d7ee7c11bb1 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-define.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-define.lua @@ -5,8 +5,8 @@ do -- block to avoid to many local variables error assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-harf-define", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / HarfBuzz font loading", license = "GPL v2.0", author = "Khaled Hosny, Marcel Krüger", @@ -21,14 +21,14 @@ local gsub = string.gsub local hb = luaotfload.harfbuzz local scriptlang_to_harfbuzz = require'luaotfload-scripts'.to_harfbuzz +local cff2_handler = require'luaotfload-harf-var-cff2' +local ttf_handler = require'luaotfload-harf-var-ttf' local harf_settings = luaotfload.harf or {} luaotfload.harf = harf_settings harf_settings.default_buf_flags = hb.Buffer.FLAGS_DEFAULT or 0 -local hbfonts = {} - local cfftag = hb.Tag.new("CFF ") local cff2tag = hb.Tag.new("CFF2") local os2tag = hb.Tag.new("OS/2") @@ -36,9 +36,15 @@ local posttag = hb.Tag.new("post") local glyftag = hb.Tag.new("glyf") local gpostag = hb.Tag.new("GPOS") +local italtag = hb.Tag.new("ital") +local wghttag = hb.Tag.new("wght") +local slnttag = hb.Tag.new("slnt") +local opsztag = hb.Tag.new("opsz") + local invalid_l = hb.Language.new() local invalid_s = hb.Script.new() +local tointeger = math.tointeger local floor = math.floor local function round(x) return floor(x + 0.5) @@ -71,27 +77,123 @@ local get_designsize do end local containers = luaotfload.fontloader.containers -local hbcacheversion = 1.2 -local facecache = containers.define("fonts", "hb", hbcacheversion, true) +local hbcacheversion = 1.4 +local fontcache = containers.define("fonts", "hb", hbcacheversion, true) +local facecache = {} + +local variable_pattern do + local l = lpeg or require'lpeg' + local white = l.S' \t'^0 + local number = l.C(l.S'+-'^-1 * (l.R'09'^1 * ('.' * l.R'09'^0)^-1 + '.' * l.R'09'^1)) + local name_or_tag = l.C(l.R('AZ', 'az')^1) + local pair = l.Ct(name_or_tag * white * '=' * white * (number + l.Cc(nil) * 'auto')) + variable_pattern = l.Ct(pair * (white * ',' * white * pair)^0) +end local function loadfont(spec) local path, sub = spec.resolved, spec.sub or 1 - local key = string.format("%s:%d", gsub(path, "[/\\]", ":"), sub) + local key = gsub(string.format("%s:%d:%s", path, sub, instance), "[/\\]", ":") local attributes = lfs.attributes(path) if not attributes then return end local size, date = attributes.size or 0, attributes.modification or 0 - local cached = containers.read(facecache, key) + local hbface = facecache[key] + if not hbface then + hbface = hb.Face.new(path, sub - 1) + facecache[key] = hbface + end + + local normalized + local varkey + if hbface.ot_var_has_data and hbface:ot_var_has_data() then + local design_coords + local instance = spec.features.raw.instance + local axis = spec.features.raw.axis + local assignments = axis and variable_pattern:match(axis) + if axis and not assignments and not instance then + instance, axis = axis, nil + end + if instance then + instance = instance:lower() + local instances = hbface:ot_var_named_instance_get_infos() + for i = 1, #instances do + local inst = instances[i] + if instance == hbface:get_name(inst.subfamily_name_id):lower() then + design_coords = {hbface:ot_var_named_instance_get_design_coords(inst.index)} + break + end + end + if not design_coords then + texio.write_nl'Warning (luaotfload): Unknown instance name ignored.' + end + end + local axes = hbface:ot_var_get_axis_infos() + design_coords = design_coords or lua.newtable(#axes, 0) + if assignments then + for i = 1, #assignments do + local found + local name = assignments[i][1] + local tag + if #name <= 4 then + tag = hb.Tag.new(name) + end + name = string.lower(name) + for j = 1, #axes do + local axis = axes[j] + if tag and tag == axis.tag then + found = axis + break + end + if name == hbface:get_name(axis.name_id):lower() then + found = axis + if not tag then break end + end + end + if found then + design_coords[found.axis_index] = assignments[i][2] + else + texio.write_nl'Warning (luaotfload): Unknown axis name ignored.' + end + end + end + for i = 1, #axes do + local axis = axes[i] + local index = axis.axis_index -- == i in practise + if not design_coords[index] then + local tag = axis.tag + if tag == italtag and spec.style then + design_coords[index] = spec.style == 'i' or spec.style == 'bi' and 1 or 0 + elseif tag == slnttag and spec.style then + design_coords[index] = spec.style == 'i' or spec.style == 'bi' and -5 or 0 + elseif tag == wghttag and spec.style then + design_coords[index] = (spec.style == 'b' or spec.style == 'bi') and 600 or 400 + elseif tag == opsztag and (spec.optsize or spec.size > 0) then + design_coords[index] = spec.optsize or spec.size / 65536 + else + design_coords[index] = axis.default_value + end + end + end + normalized = {hbface:ot_var_normalize_coords(table.unpack(design_coords))} + varkey = ':' .. table.concat(normalized, ':') + key = key .. varkey + else + varkey = '' + end + + local cached = containers.read(fontcache, key) local iscached = cached and cached.date == date and cached.size == size - local hbface = iscached and cached.face or hb.Face.new(path, sub - 1) local tags = hbface and hbface:get_table_tags() -- If the face has no table tags then it isn’t a valid SFNT font that -- HarfBuzz can handle. if not tags then return end local hbfont = iscached and cached.font or hb.Font.new(hbface) + if normalized then + hbfont:set_var_coords_normalized(table.unpack(normalized)) + end if not iscached then local upem = hbface:get_upem() @@ -174,7 +276,7 @@ local function loadfont(spec) local glyph = hbfont:get_nominal_glyph(uni) if glyph then characters[uni] = glyph - nominals[glyph] = uni + nominals[glyph] = tointeger(uni) end end @@ -224,12 +326,13 @@ local function loadfont(spec) nominals = nominals, unicodes = characters, psname = hbface:get_name(hb.ot.NAME_ID_POSTSCRIPT_NAME), - fullname = hbface:get_name(hb.ot.NAME_ID_FULL_NAME), + fullname = hbface:get_name(hb.ot.NAME_ID_FULL_NAME) .. varkey, haspng = hbface:ot_color_has_png(), loaded = {}, -- Cached loaded glyph data. + normalized = normalized, } - containers.write(facecache, key, cached) + containers.write(fontcache, key, cached) end cached.face = hbface cached.font = hbfont @@ -340,6 +443,7 @@ local function scalefont(data, spec) hscale = upem, vscale = upem, buf_flags = harf_settings.default_buf_flags, + obj_repl = characters[0xFFFC] and 0xD800 or 0xFFFC, }, specification = spec, shared = { @@ -349,6 +453,7 @@ local function scalefont(data, spec) resources = { unicodes = data.name_to_char, }, + streamprovider = data.normalized and (data.fonttype == 'opentype' and 1 or 3) or nil, } tfmdata.shared.processes = fonts.handlers.otf.setfeatures(tfmdata, features) fonts.constructors.applymanipulators("otf", tfmdata, features, false) @@ -391,7 +496,7 @@ fonts.readers.harf = function(spec) val = (val and '+' or '-')..key hb_features[#hb_features + 1] = hb.Feature.new(val) elseif tonumber(val) then - val = '+'..key..'='..tonumber(val) - 1 + val = '+'..key..'='..tonumber(val) hb_features[#hb_features + 1] = hb.Feature.new(val) end end @@ -409,3 +514,36 @@ luatexbase.add_to_callback('find_truetype_file', function(name) return find_file(name, 'truetype fonts') or name:gsub('^harfloaded:', '') end, 'luaotfload.harf.strip_prefix') + +local glyph_stream_data +local glyph_stream_mapping, glyph_stream_mapping_inverse +local extents_hbfont +local cb = luatexbase.remove_from_callback('glyph_stream_provider', 'luaotfload.glyph_stream') +luatexbase.add_to_callback('glyph_stream_provider', function(fid, cid, kind, ocid) + if cid == 0 then -- Always the first call for a font + glyph_stream_data, extents_hbfont = nil + collectgarbage() + local fontdir = font.getfont(fid) + if fontdir and fontdir.hb then + if kind == 3 then + glyph_stream_mapping = {[ocid] = cid} + glyph_stream_mapping_inverse = {[cid] = ocid} + extents_hbfont = fontdir.hb.shared.font + elseif kind == 2 then + glyph_stream_data = ttf_handler(fontdir.hb.shared.face, fontdir.hb.shared.font, glyph_stream_mapping, glyph_stream_mapping_inverse) + else + glyph_stream_data = cff2_handler(fontdir.hb.shared.face, fontdir.hb.shared.font) + end + end + end + if glyph_stream_data then + return glyph_stream_data(cid) + elseif extents_hbfont then + glyph_stream_mapping[ocid] = cid + glyph_stream_mapping_inverse[cid] = ocid + local extents = extents_hbfont:get_glyph_extents(ocid) + return extents.width, extents.x_bearing, extents.height, extents.y_bearing + else + return cb(fid, cid, kind, ocid) + end +end, 'luaotfload.harf.glyphstream') diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-plug.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-plug.lua index 1577aa7004b..634da687860 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-plug.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-plug.lua @@ -4,8 +4,8 @@ ----------------------------------------------------------------------- assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-harf-plug", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / HarfBuzz shaping", license = "GPL v2.0", author = "Khaled Hosny, Marcel Krüger", @@ -110,6 +110,11 @@ local endactual_p = "luaotfload_endactualtext" local empty_table = {} +local function is_empty_disc(n) + local pre, post, rep = getdisc(n) + return not pre and not post and not rep +end + -- "Copy" properties as done by LuaTeX: Make old properties metatable local function copytable(old) local new = {} @@ -174,6 +179,7 @@ local function itemize(head, fontid, direction) local hbdata = fontdata and fontdata.hb local spec = fontdata and fontdata.specification local options = spec and spec.features.raw + local obj_repl = hbdata and hbdata.obj_repl local runs, codes = {}, {} local dirstack = {} @@ -187,7 +193,7 @@ local function itemize(head, fontid, direction) for n, id, subtype in traverse(head) do if in_disc == n then in_disc = nil end local disc - local code = 0xFFFC -- OBJECT REPLACEMENT CHARACTER + local code = obj_repl -- OBJECT REPLACEMENT CHARACTER, substitute invalid surrogate if otherwise colliding with font glyph local skip = lastskip local props = properties[n] @@ -205,7 +211,7 @@ local function itemize(head, fontid, direction) elseif id == glue_t and subtype == spaceskip_t then code = 0x0020 -- SPACE elseif id == disc_t then - if uses_font(n, fontid) then + if uses_font(n, fontid) or not lastskip and is_empty_disc(n) then local _, _, rep, _, _, rep_tail = getdisc(n, true) setfield(n, 'replace', nil) local prev, next = getboth(n) @@ -347,6 +353,7 @@ function shape(head, firstnode, run) local options = spec.features.raw local hbshared = hbdata.shared local hbfont = hbshared.font + local obj_repl = hbdata.obj_repl local lang = spec.language local script = spec.script @@ -430,7 +437,9 @@ function shape(head, firstnode, run) end -- Is this a safe breakpoint? - if discs and ((not glyph) or codes[cluster] == 0x20 or codes[cluster+1] == 0x20 or codes[cluster+1] == 0xFFFC + if discs and ((not glyph) + or codes[cluster] == 0x20 or codes[cluster+1] == 0x20 + or codes[cluster] == obj_repl or codes[cluster+1] == obj_repl or not unsafetobreak(glyph)) then -- Should we change the discretionary state? local anchor_cluster, after_cluster = offset + discs.anchor_cluster, offset + discs.after_cluster @@ -478,20 +487,20 @@ function shape(head, firstnode, run) local precodes, postcodes, repcodes = {}, {} table.move(codes, disc_cluster + 1, anchor_cluster, 1, precodes) for n in traverse(pre) do - precodes[#precodes + 1] = is_char(n, fontid) or 0xFFFC + precodes[#precodes + 1] = is_char(n, fontid) or obj_repl end for n in traverse(post) do - postcodes[#postcodes + 1] = is_char(n, fontid) or 0xFFFC + postcodes[#postcodes + 1] = is_char(n, fontid) or obj_repl end table.move(codes, after_cluster + 1, cluster, #postcodes + 1, postcodes) if saved_after then repcodes = table.move(codes, disc_cluster + 1, cluster, 1, {}) table.move(codes, cluster + 1, #codes + cluster - disc_cluster, disc_cluster + 3) - codes[disc_cluster + 1], codes[disc_cluster + 2] = 0xFFFC, 0xFFFC + codes[disc_cluster + 1], codes[disc_cluster + 2] = obj_repl, obj_repl else table.move(codes, cluster + 1, #codes + cluster - disc_cluster, disc_cluster + 2) - codes[disc_cluster + 1] = 0xFFFC + codes[disc_cluster + 1] = obj_repl end do @@ -535,7 +544,7 @@ function shape(head, firstnode, run) post = makesub(run, postcodes, post), cluster = disc_cluster, nextcluster = disc_cluster + 1, - codepoint = 0xFFFC, + codepoint = obj_repl, } if saved_after then local next_disc = discs.next @@ -550,10 +559,10 @@ function shape(head, firstnode, run) local saved_anchor, saved_after = saved_anchor + saved_offset, saved_after + saved_offset table.move(postcodes, 1, saved_anchor, 1, next_precodes) for n in traverse(next_pre) do - next_precodes[#next_precodes + 1] = is_char(n, fontid) or 0xFFFC + next_precodes[#next_precodes + 1] = is_char(n, fontid) or obj_repl end for n in traverse(next_post) do - next_postcodes[#next_postcodes + 1] = is_char(n, fontid) or 0xFFFC + next_postcodes[#next_postcodes + 1] = is_char(n, fontid) or obj_repl end table.move(postcodes, saved_after + 1, #postcodes, #next_postcodes + 1, next_postcodes) @@ -576,7 +585,7 @@ function shape(head, firstnode, run) local saved_anchor = saved_anchor - disc_cluster table.move(repcodes, 1, saved_anchor, 1, next_repcodes) for n in traverse(next_rep) do - next_repcodes[#next_repcodes + 1] = is_char(n, fontid) or 0xFFFC + next_repcodes[#next_repcodes + 1] = is_char(n, fontid) or obj_repl end local rep = glyphs[disc_glyph].replace.head @@ -599,7 +608,7 @@ function shape(head, firstnode, run) post = makesub(run, next_postcodes, next_post), cluster = disc_cluster, nextcluster = disc_cluster + 1, - codepoint = 0xFFFC, + codepoint = obj_repl, } end i = disc_glyph + 1 @@ -772,9 +781,15 @@ local function tonodes(head, node, run, glyphs) nodeindex = glyph.cluster + 1 elseif nextcluster + 1 == nodeindex then -- Oops, we went too far nodeindex = nodeindex - 1 - local new = inherit(glyph_t, getprev(node), lastprops) - setfont(new, fontid) - head, node = insertbefore(head, node, new) + if node then + local new = inherit(glyph_t, getprev(node), lastprops) + head, node = insertbefore(head, node, new) + else + node = tail(head) + local new = inherit(glyph_t, node, lastprops) + head, node = insertafter(head, node, new) + end + setfont(node, fontid) end local gid = glyph.codepoint local char = nominals[gid] or gid_offset + gid @@ -1134,6 +1149,8 @@ local function get_glyph_info(n) local c = getchar(n) if c == 0 then return '^^@' + elseif c < 0 then -- These are left and right boundaries in ligature lists + return '' elseif c < 0x110000 then return utfchar(c) else diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-var-cff2.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-var-cff2.lua new file mode 100644 index 00000000000..9c05b7a6c72 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-var-cff2.lua @@ -0,0 +1,442 @@ +----------------------------------------------------------------------- +-- FILE: luaotfload-harf-var-cff2.lua +-- DESCRIPTION: part of luaotfload / HarfBuzz / Parse and convert CFF2 tables +----------------------------------------------------------------------- +do + assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { + name = "luaotfload-harf-var-cff2", + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE + description = "luaotfload submodule / CFF2 table processing", + license = "GPL v2.0", + author = "Marcel Krüger", + copyright = "Luaotfload Development Team", + } +end + +local hb = assert(luaotfload.harfbuzz) +local cff2 = hb.Tag.new'CFF2' +local serialize = require'luaotfload-harf-var-t2-writer' + +local offsetfmt = ">I%i" +local function parse_index(buf, i) + local count, offsize + count, offsize, i = string.unpack(">I4B", buf, i) + if count == 0 then return {}, i-1 end + local fmt = offsetfmt:format(offsize) + local offsets = {} + local dataoffset = i + offsize*count - 1 + for j=1,count+1 do + offsets[j], i = string.unpack(fmt, buf, i) + end + for j=1,count+1 do + offsets[j] = offsets[j] + i - 1 + end + return offsets, offsets[#offsets] +end + +local real_mapping = { [0] = '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '.', 'E', 'E-', nil, '-', nil} +local function parse_real(cs, offset) + local c = cs:byte(offset) + if not c then return offset end + local c1, c2 = real_mapping[c>>4], real_mapping[c&0xF] + if not c1 or not c2 then + return c1 or offset, c1 and offset + else + return c1, c2, parse_real(cs, offset+1) --Warning: This is not a tail-call, + -- so we are affected by the stack limit. On the other hand, as long as + -- there are less than ~50 bytes we should be safe. + end +end + +local function get_number(result) + if #result ~= 1 then + print(require'inspect'(result)) + end + assert(#result == 1) + local num = result[1] + result[1] = nil + return num +end + +local function get_bool(result) + return get_number(result) == 1 +end + +local function get_array(result) + local arr = table.move(result, 1, #result, 1, {}) + for i=1,#result do result[i] = nil end + return arr +end + +local function get_delta(result) + local arr = get_array(result) + local last = 0 + for i=1,#arr do + arr[i] = arr[i]+last + last = arr[i] + end + return arr +end + +local function get_private(result) + local arr = get_array(result) + assert(#arr == 2) + return arr +end + +local function do_blend(result, vstore) + if not vstore then + error'blend operator only allowed in Private disctionary of variable fonts' + end + local vsindex = (result.vsindex or 0) + 1 + local factors = vstore[vsindex] + local n = result[#result] + local k = #factors + local before = #result - 1 - n*(k+1) + for i = 1, n do + local val = result[before + i] + for j = 1, k do + val = val + factors[j] * result[before + n + (i-1) * k + j] + end + result[before + i] = math.floor(val + .5) + end + for i = before + n + 1, #result do + result[i] = nil + end + return arr +end + +local function apply_matrix(m, x, y) + return (m[1] * x + m[3] * y + m[5])*1000, (m[2] * x + m[4] * y + m[6])*1000 +end + +local operators = { + [6] = {'BlueValues', get_delta}, + [7] = {'OtherBlues', get_delta}, + [8] = {'FamilyBlues', get_delta}, + [9] = {'FamilyOtherBlues', get_delta}, + [10] = {'StdHW', get_number}, + [11] = {'StdVW', get_number}, + [17] = {'CharStrings', get_number}, + [18] = {'Private', get_private}, + [19] = {'Subrs', get_number}, + [22] = {'vsindex', get_number}, + [23] = {'blend', do_blend}, + [24] = {'vstore', get_number}, + [-8] = {'FontMatrix', get_array}, +[-10] = {'BlueScale', get_number}, +[-11] = {'BlueShift', get_number}, +[-12] = {'BlueFuzz', get_number}, +[-13] = {'StemSnapH', get_delta}, +[-14] = {'StemSnapV', get_delta}, +[-15] = {'ForceBold', get_bool}, -- ??? +[-18] = {'LanguageGroup', get_number}, +[-19] = {'ExpansionFactor', get_number}, +[-20] = {'initialRandomSeed', get_number}, -- ??? +[-37] = {'FDArray', get_number}, +[-38] = {'FDSelect', get_number}, +} +local function parse_dict(buf, i, j, vstore) + result = {} + while i<=j do + local cmd = buf:byte(i) + if cmd == 29 then + result[#result+1] = string.unpack(">i4", buf:sub(i+1, i+4)) + i = i+4 + elseif cmd == 28 then + result[#result+1] = string.unpack(">i2", buf:sub(i+1, i+2)) + i = i+2 + elseif cmd >= 251 then -- Actually "and cmd ~= 255", but 255 is reserved + result[#result+1] = -((cmd-251)*256)-string.byte(buf, i+1)-108 + i = i+1 + elseif cmd >= 247 then + result[#result+1] = (cmd-247)*256+string.byte(buf, i+1)+108 + i = i+1 + elseif cmd >= 32 then + result[#result+1] = cmd-139 + elseif cmd == 30 then -- 31 is reserved again + local real = {parse_real(buf, i+1)} + i = real[#real] + real[#real] = nil + result[#result+1] = tonumber(table.concat(real)) + else + if cmd == 12 then + i = i+1 + cmd = -buf:byte(i)-1 + end + local op = operators[cmd] + if not op then error[[Unknown CFF operator]] end + result[op[1]] = op[2](result, vstore) + end + i = i+1 + end + return result +end + +local function parse_charstring(buf, start, after, globalsubrs, subrs, result) + local lastresult = result[#result] + while start ~= after do + local cmd = buf:byte(start) + if cmd == 28 then + lastresult[#lastresult+1] = string.unpack(">i2", buf:sub(start+1, start+2)) + start = start+2 + elseif cmd == 255 then + lastresult[#lastresult+1] = string.unpack(">i4", buf:sub(start+1, start+4))/0x10000 + start = start+4 + elseif cmd >= 251 then + lastresult[#lastresult+1] = -((cmd-251)*256)-string.byte(buf, start+1)-108 + start = start+1 + elseif cmd >= 247 then + lastresult[#lastresult+1] = (cmd-247)*256+string.byte(buf, start+1)+108 + start = start+1 + elseif cmd >= 32 then + lastresult[#lastresult+1] = cmd-139 + elseif cmd == 10 then + local idx = lastresult[#lastresult]+subrs.bias + local sub_start = subrs[idx] + local sub_stop = subrs[idx+1] + lastresult[#lastresult] = nil + parse_charstring(buf, sub_start, sub_stop, globalsubrs, subrs, result) + lastresult = result[#result] + elseif cmd == 29 then + local idx = lastresult[#lastresult]+globalsubrs.bias + local sub_start = globalsubrs[idx] + local sub_stop = globalsubrs[idx+1] + lastresult[#lastresult] = nil + parse_charstring(buf, sub_start, sub_stop, globalsubrs, subrs, result) + lastresult = result[#result] + elseif cmd == 11 then + break -- We do not keep subroutines, so drop returns and continue with the outer commands + elseif cmd == 15 then -- vsindex + assert(#lastresult == 2) + result.factors = result.vstore[lastresult[2] + 1] + lastresult[2] = nil + elseif cmd == 16 then -- blend + local factors = result.factors + if not factors then + error'blend operator outside of variable font or with invalid vsindex' + end + local n = lastresult[#lastresult] + local k = #factors + local before = #lastresult - 1 - n*(k+1) + for i = 1, n do + local val = lastresult[before + i] + for j = 1, k do + val = val + factors[j] * lastresult[before + n + (i-1) * k + j] + end + lastresult[before + i] = math.floor(val + .5) + end + for i = before + n + 1, #lastresult do + lastresult[i] = nil + end + elseif cmd == 12 then + start = start+1 + cmd = buf:byte(start) + lastresult[1] = -cmd-1 + lastresult = {false} + result[#result+1] = lastresult + elseif cmd == 19 or cmd == 20 then + if #result == 1 then + lastresult = {} + result[#result+1] = lastresult + end + lastresult[1] = cmd + local newi = start+(result.stemcount+7)//8 + lastresult[2] = buf:sub(start+1, newi) + start = newi + else + if cmd == 21 and #result == 1 then + table.insert(result, 1, {false}) + if #lastresult == 4 then + result[1][2] = lastresult[2] + table.remove(lastresult, 2) + end + elseif (cmd == 4 or cmd == 22) and #result == 1 then + table.insert(result, 1, {false}) + if #lastresult == 3 then + result[1][2] = lastresult[2] + table.remove(lastresult, 2) + end + elseif cmd == 14 and #result == 1 then + table.insert(result, 1, {false}) + if #lastresult == 2 or #lastresult == 6 then + result[1][2] = lastresult[2] + table.remove(lastresult, 2) + end + elseif cmd == 1 or cmd == 3 or cmd == 18 or cmd == 23 then + if #result == 1 then + table.insert(result, 1, {false}) + if #lastresult % 2 == 0 then + result[1][2] = lastresult[2] + table.remove(lastresult, 2) + end + end + result.stemcount = result.stemcount + #lastresult//2 + end + lastresult[1] = cmd + lastresult = {false} + result[#result+1] = lastresult + end + start = start+1 + end + return result +end + +local function parse_fdselect(buf, offset, CharStrings) + local format + format, offset = string.unpack(">B", buf, offset) + if format == 0 then + for i=0,#CharStrings-1 do + local code + code, offset = string.unpack(">B", buf, offset) + CharStrings[i][3] = code + 1 + end -- Reimplement with string.byte + elseif format == 3 then + local count, last + count, offset = string.unpack(">I2", buf, offset) + for i=1,count do + local first, code, after = string.unpack(">I2BI2", buf, offset) + for j=first, after-1 do + CharStrings[j][3] = code + 1 + end + offset = offset + 3 + end + elseif format == 4 then + local count, last + count, offset = string.unpack(">I4", buf, offset) + for i=1,count do + local first, code, after = string.unpack(">I4I2I4", buf, offset) + for j=first, after-1 do + CharStrings[j][3] = code + 1 + end + offset = offset + 6 + end + else + error[[Invalid FDSelect format]] + end +end + +local function parse_vstore(buf, offset, variation) + local size, format, region_list_off, item_variation_count, off = string.unpack(">I2I2I4I2", buf, offset) + if format ~= 1 then + error'Unsupported vstore format' + end + offset = offset + 2 -- Skip the size + region_list_off = offset + region_list_off + + local axis_count, region_count + axis_count, region_count, region_list_off = string.unpack(">I2I2", buf, region_list_off) + + local variation_regions = {} + for i = 1, region_count do + local factor = 1 + for j = 1, axis_count do + local start, peak, stop + start, peak, stop, region_list_off = string.unpack(">i2i2i2", buf, region_list_off) + local coord = variation[j] + if peak == 0 then -- Skip + elseif peak == coord then + -- factor = factor * 1 + elseif coord <= start or coord >= stop then + factor = 0 + break + elseif coord < peak then + factor = factor * ((coord-start) / (peak-start)) + else--if coord > peak then + factor = factor * ((stop-coord) / (stop-peak)) + end + end + variation_regions[i] = factor + end + + local variation_data = {} + for i = 1, item_variation_count do + local item_off + item_off, off = string.unpack(">I4", buf, off) + local i_count, short_count, region_count + i_count, short_count, region_count, item_off = string.unpack(">I2I2I2", buf, item_off + offset) + if i_count ~= 0 or short_count ~= 0 then + error'Unexpected variation items in CFF2 table' + end + local factors = {} + for j = 1, region_count do + local region + region, item_off = string.unpack(">I2", buf, item_off) + factors[j] = variation_regions[region+1] + end + variation_data[i] = factors + end + return variation_data +end + +function parse_cff2(buf, i0, coords) + local fontid = 1 + local major, minor, hdrSize, topSize = string.unpack(">BBBH", buf, i0) + if major ~= 2 then error[[Unsupported CFF version]] end + local i = i0 + hdrSize + local top = parse_dict(buf, i, i + topSize - 1) + i = i + topSize + local globalsubrs + globalsubrs, i = parse_index(buf, i) + globalsubrs.bias = #globalsubrs-1 < 1240 and 108 or #globalsubrs-1 < 33900 and 1132 or 32769 + top.GlobalSubrs = globalsubrs + local CharStrings = parse_index(buf, i0+top.CharStrings) + for i=1,#CharStrings-1 do + CharStrings[i-1] = {CharStrings[i], CharStrings[i+1]-1} + end + CharStrings[#CharStrings] = nil + CharStrings[#CharStrings] = nil + local fonts = parse_index(buf, i0+top.FDArray) + top.FDArray = nil + top.vstore = parse_vstore(buf, i0 + top.vstore, coords) + local privates = {} + top.Privates = privates + for i=1,#fonts-1 do + local font = fonts[i] + local fontdir = parse_dict(buf, fonts[i], fonts[i+1]-1) + privates[i] = parse_dict(buf, i0+fontdir.Private[2], i0+fontdir.Private[2]+fontdir.Private[1]-1, top.vstore) + local subrs = privates[i].Subrs + if subrs then + subrs = parse_index(buf, i0+fontdir.Private[2]+subrs) + subrs.bias = #subrs-1 < 1240 and 108 or #subrs-1 < 33900 and 1132 or 32769 + privates[i].Subrs = subrs + end + end + if top.FDSelect then + parse_fdselect(buf, i0+top.FDSelect, CharStrings) + else + for i=0,#CharStrings-1 do + CharStrings[i][3] = 1 + end + end + top.CharStrings = CharStrings + local bbox + if top.FontMatrix then + local x0, y0 = apply_matrix(top.FontMatrix, top.FontBBox[1], top.FontBBox[2]) + local x1, y1 = apply_matrix(top.FontMatrix, top.FontBBox[3], top.FontBBox[4]) + bbox = {x0, y0, x1, y1} + else + bbox = top.FontBBox + end + return top, bbox +end + +local function parse_glyph(buffer, top, gid) + local cs = top.CharStrings[gid] + local Private = top.Privates[cs[3]] + return parse_charstring(buffer, cs[1], cs[2] + 1, + top.GlobalSubrs, Private.Subrs, + {{false}, stemcount = 0, vstore = top.vstore, factors = top.vstore and top.vstore[(Private.vsindex or 0) + 1]}) +end + +return function(face, font) + local data = face:get_table(cff2):get_data() + local content = parse_cff2(data, 1, {font:get_var_coords_normalized()}) + return function(gid) + local glyph = parse_glyph(data, content, gid) + glyph[1][2] = font:get_glyph_h_advance(gid) + return serialize(glyph) + end +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-var-t2-writer.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-var-t2-writer.lua new file mode 100644 index 00000000000..f10bf6d860e --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-var-t2-writer.lua @@ -0,0 +1,120 @@ +----------------------------------------------------------------------- +-- FILE: luaotfload-harf-var-t2-writer.lua +-- DESCRIPTION: part of luaotfload / HarfBuzz / Serialize Type 2 charstrings +----------------------------------------------------------------------- +do + assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { + name = "luaotfload-harf-var-t2-writer", + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE + description = "luaotfload submodule / Type 2 charstring writer", + license = "GPL v2.0", + author = "Marcel Krüger", + copyright = "Luaotfload Development Team", + } +end + +local pack = string.pack +local function numbertot2(n) + if math.abs(n) > 2^15 then + error[[Number too big]] + end + local num = math.floor(n + .5) + if n ~= 0 and math.abs((num-n)/n) > 0.001 then + num = math.floor(n * 2^16 + 0.5) + return pack(">Bi4", 255, math.floor(n * 2^16 + 0.5)) + elseif num >= -107 and num <= 107 then + return string.char(num + 139) + elseif num >= 108 and num <= 1131 then + return pack(">I2", num+0xF694) -- -108+(247*0x100) + elseif num >= -1131 and num <= -108 then + return pack(">I2", -num+0xFA94) -- -108+(251*0x100) + else + return pack(">Bi2", 28, num) + end +end +local function convert_cs(cs, upem) + local cs_parts = {} + local function add(cmd, first, ...) + if cmd == 19 or cmd == 20 then + cs_parts[#cs_parts+1] = string.char(cmd) + cs_parts[#cs_parts+1] = first + return + end + if first then + cs_parts[#cs_parts+1] = numbertot2(first*upem/1000) + return add(cmd, ...) + end + if cmd then + if cmd < 0 then + cs_parts[#cs_parts+1] = string.char(12, -cmd-1) + else + cs_parts[#cs_parts+1] = string.char(cmd) + end + end + end + for _, args in ipairs(cs) do + if args then + local cmd = args[1] + local height = #args - 1 + local i = 2 + while height > 48 do + -- Special cases: hhcurveto, vvcurveto, rcurveline, rlinecurve + -- hhvurveto/vvcurveto: In odd argument case, the first argument must be + -- followed by a multiple of four arguments. + if cmd == 27 and i == 2 and height % 4 == 1 then -- hhcurveto + add(cmd, table.unpack(args, i, i + 44)) + height = height - 45 + i = i + 45 + elseif cmd == 26 and i == 2 and height % 4 == 1 then -- vvcurveto + add(cmd, table.unpack(args, i, i + 44)) + height = height - 45 + i = i + 45 + -- rcurveline/rlinecurve: At least 8 arguments must be preserved, all previous instances + -- need different commands + elseif cmd == 24 then -- rcurveline + height = height - 48 + local count + if height >= 8 then + count = 48 + else + count = 48 - 8 + height + height = 8 + end + add(8, table.unpack(args, i, i + count - 1)) -- 8 = rrcurveto + i = i + count + elseif cmd == 25 then -- rlinecurve + height = height - 48 + local count + if height >= 8 then + count = 48 + else + count = 48 - 8 + height + height = 8 + end + add(5, table.unpack(args, i, i + count - 1)) -- 5 = rlineto + i = i + count + else -- Some commands have an optional last argument which can appear after multiple of 48 arguments, + -- so it's safer to never leave a element alone in a line. + height = height - 48 + local count + if height > 1 then + count = 48 + else + -- assert(height == 1) + count = 24--48 - 25 + height + height = 25 + end + add(cmd, table.unpack(args, i, i + count - 1)) + i = i + count + end + end + add(cmd, table.unpack(args, i)) + end + end + return table.concat(cs_parts) +end + +return function(cs, upem) + return convert_cs(cs, upem or 1000) +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-var-ttf.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-var-ttf.lua new file mode 100644 index 00000000000..80cf6768ae6 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-harf-var-ttf.lua @@ -0,0 +1,564 @@ +----------------------------------------------------------------------- +-- FILE: luaotfload-harf-var-ttf.lua +-- DESCRIPTION: part of luaotfload / HarfBuzz / Parse and apply gvar tables +----------------------------------------------------------------------- +do + assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { + name = "luaotfload-harf-var-ttf", + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE + description = "luaotfload submodule / gvar table processing", + license = "GPL v2.0", + author = "Marcel Krüger", + copyright = "Luaotfload Development Team", + } +end + +local hb = assert(luaotfload.harfbuzz) +local gvar_tag = hb.Tag.new'gvar' +local glyf_tag = hb.Tag.new'glyf' +local loca_tag = hb.Tag.new'loca' + +local function read_tuple(data, offset, axes_count) + local tuple = lua.newtable(axes_count, 0) + for i=1, axes_count do + tuple[i] = sio.readinteger2(data, offset + 2*i-2) + end + return tuple, offset + 2*axes_count +end + +local function read_gvar(data) + if 1 ~= sio.readcardinal2(data, 1) then + return nil, 'Unknown gvar version' + end + local axes_count = sio.readcardinal2(data, 5) + local shared_count = sio.readcardinal2(data, 7) + local shared_offset = sio.readcardinal4(data, 9) + 1 + local glyph_count = sio.readcardinal2(data, 13) + local flags = sio.readcardinal2(data, 15) + local variation_offset = sio.readcardinal4(data, 17) + 1 + local variation_offsets = sio.readcardinaltable(data, 21, glyph_count+1, flags & 1 == 1 and 4 or 2) + if flags & 1 == 1 then + for i = 1, glyph_count+1 do + variation_offsets[i] = variation_offset + variation_offsets[i] + end + else + for i = 1, glyph_count+1 do + variation_offsets[i] = variation_offset + 2*variation_offsets[i] + end + end + + local shared = lua.newtable(shared_count, 0) + for i=1, shared_count do + shared[i], shared_offset = read_tuple(data, shared_offset, axes_count) + end + + local glyph_variation = lua.newtable(glyph_count, 0) + for i = 1, glyph_count do + local offset = variation_offsets[i] + if variation_offsets[i+1] ~= offset then + local tuple_variations = sio.readcardinal2(data, offset) + local flags = tuple_variations >> 12 + tuple_variations = tuple_variations & 0xFFF + local data_offset = offset + sio.readcardinal2(data, offset + 2) + + local headers = lua.newtable(tuple_variations, 0) + offset = offset + 4 + for j=1, tuple_variations do + local data_size = sio.readcardinal2(data, offset) + local tuple_index = sio.readcardinal2(data, offset + 2) + local peak, start_inter, end_inter + offset = offset + 4 + if tuple_index & 0x8000 == 0x8000 then + peak, offset = read_tuple(data, offset, axes_count) + else + peak = shared[(tuple_index & 0xFFF) + 1] + end + if tuple_index & 0x4000 == 0x4000 then + start_inter, offset = read_tuple(data, offset, axes_count) + end_inter, offset = read_tuple(data, offset, axes_count) + end + headers[j] = { + size = data_size, + peak = peak, + start_inter = start_inter, + end_inter = end_inter, + private_points = tuple_index & 0x2000 == 0x2000 or nil, + } + end + glyph_variation[i] = { + offset = data_offset, + shared_points = flags & 0x8 == 0x8 or nil, + headers = headers, + } + end + end + return glyph_variation +end + +local function read_loca(face) + local data = face:get_table(loca_tag):get_data() + local count = face:get_glyph_count() + 1 + if #data == count * 2 then + local result = sio.readcardinaltable(data, 1, count, 2) + for i=1, count do + result[i] = 2 * result[i] + end + return result + elseif #data == count * 4 then + return sio.readcardinaltable(data, 1, count, 4) + else + return nil, 'Invalid loca format' + end +end + +local function parse_glyf(loca, glyf, gid) + local offset = loca[gid + 1] + 1 + if loca[gid + 2] + 1 == offset then return end + local num_contours = sio.readinteger2(glyf, offset) + -- local xmin = sio.readinteger2(glyf, offset + 2) + -- local ymin = sio.readinteger2(glyf, offset + 4) + -- local xmax = sio.readinteger2(glyf, offset + 6) + -- local ymax = sio.readinteger2(glyf, offset + 8) + if num_contours < 0 then + -- composite + local xmin = sio.readinteger2(glyf, offset + 2) + local ymin = sio.readinteger2(glyf, offset + 4) + local xmax = sio.readinteger2(glyf, offset + 6) + local ymax = sio.readinteger2(glyf, offset + 8) + local components = { -- FIXME: These are likely incorrect + xmin = xmin, + ymin = ymin, + xmax = xmax, + ymax = ymax, + } + local flags + offset = offset + 10 + repeat + local component = {} + flags = sio.readcardinal2(glyf, offset) + component.glyph = sio.readcardinal2(glyf, offset + 2) + local payload_length + if flags & 0x2 == 0x2 then + if flags & 0x1 == 0x1 then + component.x = sio.readinteger2(glyf, offset + 4) + component.y = sio.readinteger2(glyf, offset + 6) + offset = offset + 8 + else + component.x = sio.readinteger1(glyf, offset + 4) + component.y = sio.readinteger1(glyf, offset + 5) + offset = offset + 6 + end + payload_length = 0 + else + offset = offset + 4 + if flags & 0x1 == 0x1 then + payload_length = 4 + else + payload_length = 2 + end + end + if flags & 0x8 == 0x8 then + payload_length = payload_length + 2 + elseif flags & 0x40 == 0x40 then + payload_length = payload_length + 4 + elseif flags & 0x80 == 0x80 then + payload_length = payload_length + 8 + end + if flags & 0x120 == 0x100 then -- Only applies to the last character + payload_length = payload_length + 2 + sio.readcardinal2(offset + payload_length) + end + component.flags = flags + component.payload = glyf:sub(offset, offset + payload_length - 1) + components[#components+1] = component + until flags & 0x20 == 0 + return components + else + local end_contours = sio.readcardinaltable(glyf, offset+10, num_contours, 2) + offset = offset + 10 + num_contours * 2 + local instruction_length = sio.readcardinal2(glyf, offset) + local instructions = glyf:sub(offset+2, offset+1+instruction_length) + offset = offset+2+instruction_length + local total_points = end_contours[num_contours] + 1 + local points = lua.newtable(total_points, 2) + do + local i = 1 + while i <= total_points do + local flags = sio.readcardinal1(glyf, offset) + offset = offset + 1 + local count = 0 + if flags & 0x8 == 0x8 then + count = sio.readcardinal1(glyf, offset) + offset = offset + 1 + end + for j=i, i + count do + points[j] = {flags = flags} + end + i = i + 1 + count + end + end + do + local last = 0 + for i=1, total_points do + local point = points[i] + local flags = point.flags + local value + if flags & 0x2 == 0x2 then -- short + value = sio.readcardinal1(glyf, offset) + offset = offset + 1 + if flags & 0x10 == 0 then + value = -value + end + elseif flags & 0x10 == 0 then + value = sio.readinteger2(glyf, offset) + offset = offset + 2 + else + value = 0 + end + last = last + value + point.x = last + end + last = 0 + for i=1, total_points do + local point = points[i] + local flags = point.flags + local value + if flags & 0x4 == 0x4 then -- short + value = sio.readcardinal1(glyf, offset) + offset = offset + 1 + if flags & 0x20 == 0 then + value = -value + end + elseif flags & 0x20 == 0 then + value = sio.readinteger2(glyf, offset) + offset = offset + 2 + else + value = 0 + end + last = last + value + point.y = last + point.flags = flags & 0xC1 -- Discard all flags we aready used + end + -- assert (i == total_points) + end + points.contours = end_contours + points.instructions = instructions + return points + end +end + +local function serialize_glyf(points, map) + local contours = points.contours + if points.contours then + local flagdata, xdata, ydata = {}, {}, {} + local last_flags, last_x, last_y = 0x100 -- Impossible flag to avoid triggering a repeated flag in the first step + local xmin, ymin, xmax, ymax + for i=1, #points do + local point = points[i] + local flags, x, y = point.flags, math.floor(point.x + .5), math.floor(point.y + .5) + xmin = xmin and xmin < x and xmin or x + xmax = xmax and xmax > x and xmax or x + ymin = ymin and ymin < y and ymin or y + ymax = ymax and ymax > y and ymax or y + x, last_x = x - (last_x or 0), x + if x == 0 then + flags = flags | 0x10 + elseif x < 0x100 and x > -0x100 then + if x < 0 then + x = -x + flags = flags | 0x2 + else + flags = flags | 0x12 + end + xdata[#xdata+1] = x + else + if x > 0x7FFF then + x = 0x7FFF + elseif x < -0x8000 then + x = -0x8000 + end + if x < 0 then + x = x + 0x10000 + end + xdata[#xdata+1] = x >> 8 + xdata[#xdata+1] = x & 0xFF + end + y, last_y = y - (last_y or 0), y + if y == 0 then + flags = flags | 0x20 + elseif y < 0x100 and y > -0x100 then + if y < 0 then + y = -y + flags = flags | 0x4 + else + flags = flags | 0x24 + end + ydata[#ydata+1] = y + else + if y > 0x7FFF then + y = 0x7FFF + elseif y < -0x8000 then + y = -0x8000 + end + if y < 0 then + y = y + 0x10000 + end + ydata[#ydata+1] = y >> 8 + ydata[#ydata+1] = y & 0xFF + end + if flags == last_flags & 0x1F7 then + if last_flags & 0x8 == 0x8 then + flagdata[#flagdata] = flagdata[#flagdata] + 1 + else + last_flags = last_flags | 0x8 + flagdata[#flagdata] = last_flags + flagdata[#flagdata+1] = 1 + end + else + last_flags = flags + flagdata[#flagdata+1] = last_flags + end + end + local header = string.pack(">i2i2i2i2i2" .. string.rep('I2', #contours), #contours, xmin, ymin, xmax, ymax, table.unpack(contours)) + return header .. string.pack(">s2", points.instructions) .. string.char(table.unpack(flagdata)) .. string.char(table.unpack(xdata)) .. string.char(table.unpack(ydata)) + else + local result = string.pack(">i2i2i2i2i2", -1, points.xmin, points.ymin, points.xmax, points.ymax) + for i = 1, #points do + local component = points[i] + local x, y = component.x, component.y + x = x and math.floor(x + .5) + y = y and math.floor(y + .5) + result = result + .. string.pack(component.flags & 0x2 == 0 and '>I2I2' + or component.flags & 0x1 == 0x1 and '>I2I2i2i2' + or '>I2I2i1i1', + component.flags, map[component.glyph], x, y) + .. component.payload + end + return result + end +end + +local function read_points(gvar_data, offset) + local point_count = sio.readcardinal1(gvar_data, offset) + offset = offset + 1 + if point_count == 0 then + return true, offset + end + if point_count & 0x80 ~= 0 then + point_count = ((point_count & 0x7F) << 8) | sio.readcardinal1(gvar_data, suboffset) + offset = offset + 1 + end + local points = lua.newtable(point_count, 0) + local i, last = 1, 1 + while i <= point_count do + local control = sio.readcardinal1(gvar_data, offset) + offset = offset + 1 + local count = (control & 0x7F) + 1 + local size = control & 0x80 == 0x80 and 2 or 1 + local read = size == 2 and sio.readcardinal2 or sio.readcardinal1 + for j=0, count - 1 do + last = last + read(gvar_data, offset + j * size) + points[i + j] = last + end + i, offset = i + count, offset + size * count + end + return points, offset +end + +local function read_deltas(gvar_data, offset, count) + local deltas = lua.newtable(count, 0) + local i = 1 + while i <= count do + local control = sio.readcardinal1(gvar_data, offset) + offset = offset + 1 + local length = (control & 0x3F) + 1 + if control & 0x80 == 0x80 then + for j=0, length - 1 do + deltas[i + j] = 0 + end + else + local size = control & 0x40 == 0x40 and 2 or 1 + local read = size == 2 and sio.readinteger2 or sio.readinteger1 + for j=0, length - 1 do + deltas[i + j] = read(gvar_data, offset + j * size) + end + offset = offset + size * length + end + i = i + length + end + return deltas, offset +end + +local function interpolate_glyf(loca, gvar_index, gvar, glyf, gid, coords, map) + local var = gvar_index[gid+1] + if not var then + local start = loca[gid+1] + 1 + local stop = loca[gid+2] + return glyf:sub(start, stop) + end + local points = parse_glyf(loca, glyf, gid) + if not points then return '' end + local offset = var.offset + local shared_points + if var.shared_points then + shared_points, offset = read_points(gvar, offset) + end + for i=1, #var.headers do + local header = var.headers[i] + local size = header.size + local factor = 1 + local start, peak, stop = header.start_inter, header.peak, header.end_inter + for j = 1, #coords do + local coord = coords[j] + local peak = peak[j] + local start = start and start[j] or peak < 0 and peak or 0 + local stop = stop and stop[j] or peak > 0 and peak or 0 + if peak == 0 then -- Skip + elseif peak == coord then + -- factor = factor * 1 + elseif coord <= start or coord >= stop then + factor = 0 + break + elseif coord < peak then + factor = factor * ((coord-start) / (peak-start)) + else--if coord > peak then + factor = factor * ((stop-coord) / (stop-peak)) + end + end + if factor ~= 0 then + local base_points, suboffset + if header.private_points then + base_points, suboffset = read_points(gvar, offset) + else + base_points, suboffset = shared_points, offset + end + local count = base_points == true and #points + 4 or #base_points + local x_deltas, y_deltas + x_deltas, suboffset = read_deltas(gvar, suboffset, count) + y_deltas, suboffset = read_deltas(gvar, suboffset, count) + local contours = points.contours + if contours then + if base_points == true then + for i=1, #points do + local point = points[i] + point.x = point.x + factor * x_deltas[i] + point.y = point.y + factor * y_deltas[i] + end + else + local contour = 1 + local last, last_x, last_y, last_dx, last_dy + local first, first_x, first_y, first_dx, first_dy + local cur = 1 + local skip = base_points[1] > contours[1] + 1 + local n_points = #points + for i = 1, n_points do + if i == contours[contour] + 2 then + contour = contour + 1 + last_x, last_y, last_dx, last_dy = nil + first_x, first_y, first_dx, first_dy = nil + skip = (base_points[cur] or n_points + 1) > contours[contour] + 1 + end + if not skip then + local point = points[i] + local this_x, this_y = point.x, point.y + if base_points[cur] == i then + repeat + last_x, last_y = this_x, this_y + last_dx, last_dy = x_deltas[cur], y_deltas[cur] + if not first_x then + first_x, first_y, first_dx, first_dy = last_x, last_y, last_dx, last_dy + end + point.x = last_x + factor * last_dx + point.y = last_y + factor * last_dy + cur = cur + 1 + until base_points[cur] ~= i + else + if not last_x then -- We started a new contour and haven't seen a real point yet. Look for the last one instead. + local idx = cur + while (base_points[idx + 1] or n_points + 1) <= contours[contour] + 1 do + idx = idx + 1 + end + local idx_point = points[base_points[idx]] + last_x, last_y = idx_point.x, idx_point.y + last_dx, last_dy = x_deltas[idx], y_deltas[idx] + end + local after_x, after_y, after_dx, after_dy + if (base_points[cur] or n_points + 1) <= contours[contour] + 1 then -- If the next point is part of the contour, use it. Otherwise use the first point in our contour) + local next_point = points[base_points[cur]] + after_x, after_y = next_point.x, next_point.y + after_dx, after_dy = x_deltas[cur], y_deltas[cur] + else + after_x, after_y = first_x, first_y + after_dx, after_dy = first_dx, first_dy + end + + -- The first case is a bit weird, but afterwards we just interpolate while clipping to the boundaries. + -- See the gvar spec for details. + if last_x == after_x then + if last_dx == after_dx then + point.x = this_x + factor * last_dx + end + elseif this_x <= last_x and this_x <= after_x then + point.x = this_x + factor * (last_x < after_x and last_dx or after_dx) + elseif this_x >= last_x and this_x >= after_x then + point.x = this_x + factor * (last_x > after_x and last_dx or after_dx) + else -- We have to interpolate + local t = (this_x - last_x) / (after_x - last_x) + point.x = this_x + factor * ((1-t) * last_dx + t * after_dx) + end + + -- And again for y + if last_y == after_y then + if last_dy == after_dy then + point.y = this_y + factor * last_dy + end + elseif this_y <= last_y and this_y <= after_y then + point.y = this_y + factor * (last_y < after_y and last_dy or after_dy) + elseif this_y >= last_y and this_y >= after_y then + point.y = this_y + factor * (last_y > after_y and last_dy or after_dy) + else -- We have to interpolate + local t = (this_y - last_y) / (after_y - last_y) + point.y = this_y + factor * ((1-t) * last_dy + t * after_dy) + end + end + end + end + end + else + -- Composite glyph + if base_points == true then + for i=1, #points do + local point = points[i] + if point.x then + point.x = point.x + factor * x_deltas[i] + point.y = point.y + factor * y_deltas[i] + end + end + else + for i=1, #base_points do + local point = points[base_points[i]] + if point and point.x then + point.x = point.x + factor * x_deltas[i] + point.y = point.y + factor * y_deltas[i] + end + end + end + end + assert(suboffset == offset + size) + end + offset = offset + size + end + return serialize_glyf(points, map) +end + +return function(face, font, map, map_inv) + local gvar = assert(face:get_table(gvar_tag):get_data()) + local gvar_index = assert(read_gvar(gvar)) + local loca = assert(read_loca(face)) + local glyf = assert(face:get_table(glyf_tag):get_data()) + local normalized = {font:get_var_coords_normalized()} + return function(gid) + return interpolate_glyf(loca, gvar_index, gvar, glyf, map_inv[gid], normalized, map) + end +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-init.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-init.lua index 9ce1d2494d2..73a6544e728 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-init.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-init.lua @@ -7,8 +7,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-init", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / initialization", license = "GPL v2.0" } @@ -551,10 +551,16 @@ local function init_post_load_agl () end +local function init_post_apply_keepnames () + local keepnames = config.luaotfload.misc.keepnames + luaotfload.fontloader.fonts.privateoffsets.keepnames = keepnames +end + --- (unit -> unit) list local init_post_actions = { init_post_install_callbacks, init_post_load_agl, + init_post_apply_keepnames } --- unit -> size_t diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua index 7963a839b9b..2e69cb92a6b 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua @@ -5,8 +5,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-letterspace", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / letterspacing", license = "GPL v2.0", copyright = "PRAGMA ADE / ConTeXt Development Team", @@ -70,7 +70,7 @@ local todirect = nodedirect.todirect local tonode = nodedirect.tonode local insert_node_before = nodedirect.insert_before -local free_node = nodedirect.free +local real_free_node = nodedirect.free local copy_node = nodedirect.copy local new_node = nodedirect.new @@ -86,9 +86,25 @@ local chardata = fonthashes.characters local otffeatures = fonts.constructors.newfeatures "otf" local markdata +-- For every attribute list cached in attribute_table, we have to make +-- sure that it doesn't get deleted. Therefore attribute_cleanup maps +-- from a node which has the attribute_list referenced in +-- attribute_table to the key from attribute_table. +-- Whenever a node which has an entry in attribute_cleanup is deleted, +-- we delete the corresponding entry from attribute_table since we can +-- no longer guarantee that it's references somewhere. local attribute_table = {} +local attribute_cleanup = {} local attr = luatexbase.new_attribute("luaotfload.letterspace_done") +local function free_node(n) + local k = attribute_cleanup[n] + if k then + attribute_cleanup[n], attribute_table[k] = nil + end + return real_free_node(n) +end + local function getprevreal(n) repeat n = getprev(n) @@ -445,6 +461,7 @@ kerncharacters = function (head) setattributelist(start, new_attr_list) else setattribute(start, attr, 1) + attribute_cleanup[start] = attr_list attribute_table[attr_list] = getattributelist(start) end end --[[if prev]] @@ -500,7 +517,8 @@ local function enablefontkerning ( ) "kerncharacters() failed to return a valid new head") end - for k in next, attribute_table do attribute_table[k] = nil end + for k, v in next, attribute_cleanup do attribute_cleanup[k], attribute_table[v] = nil end + assert(not next(attribute_table)) return tonode (direct_hd) end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-loaders.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-loaders.lua index 1ed66aebea2..5a84113a1f0 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-loaders.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-loaders.lua @@ -8,8 +8,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-loaders", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / callback handling", license = "GPL v2.0" } diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-log.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-log.lua index 74c5fd69299..41f9f4b8774 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-log.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-log.lua @@ -6,8 +6,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-log", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / logging", license = "GPL v2.0", author = "Khaled Hosny, Elie Roux, Philipp Gesang", diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-multiscript.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-multiscript.lua index e3cf9a0cbf8..ab688fe704c 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-multiscript.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-multiscript.lua @@ -5,8 +5,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-multiscript", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / multiscript", license = "GPL v2.0", author = "Marcel Krüger" diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-notdef.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-notdef.lua index d8cc6777234..28a371dd35e 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-notdef.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-notdef.lua @@ -5,8 +5,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-notdef", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / notdef", license = "GPL v2.0", author = "Marcel Krüger" diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-parsers.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-parsers.lua index cd452b40c29..099363fad55 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-parsers.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-parsers.lua @@ -7,8 +7,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-parsers", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / parsers", license = "GPL v2.0" } @@ -528,7 +528,7 @@ local featuresep = comma + semicolon we only support the shorthands for italic / bold / bold italic shapes, as well as setting optical size, the rest is ignored. --doc]]-- -local style_modifier = (S'bB' * S'iI'^-1 + S'iI' * S'bB'^-1) +local style_modifier = (S'bB' * S'iI'^-1 + S'iI' * S'bB'^-1 + S'rR') / stringlower local size_modifier = S"Ss" * P"=" --- optical size * Cc"optsize" * C(decimal) diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-resolvers.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-resolvers.lua index a24757777e5..a91216b79d8 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-resolvers.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-resolvers.lua @@ -11,8 +11,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-resolvers", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / resolvers", license = "GPL v2.0" } diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-scripts.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-scripts.lua index 8a4f51ea589..c3c918ef3b8 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-scripts.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-scripts.lua @@ -5,8 +5,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-script", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / Script helpers", license = "CC0 1.0 Universal", author = "Marcel Krüger" diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-status.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-status.lua index 6743a025c38..d722bfe03b6 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-status.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-status.lua @@ -1,99 +1,99 @@ return { ["hashes"]={ - { "fontloader-2021-05-20.lua", "d3d971e3e79aefcb828157b97cc33378" }, - { "fontloader-util-fil.lua", "aae2a2add764144bb58dd8216fd59260" }, - { "fontloader-util-str.lua", "f3480da8f9a6f34fe320900f7224719b" }, + { "fontloader-2022-02-24.lua", "4dc8ce07471f8af7aa98d835df654768" }, + { "fontloader-util-fil.lua", "d0accf4180201efd567816a25ad5e037" }, + { "fontloader-util-str.lua", "f4a9dd15c3e40cd3915749ac13837c95" }, { "fontloader-l-unicode.lua", "f368874f11f3c525de5864fd413e4b24" }, - { "fontloader-l-math.lua", "082add08e0bb79f39191ae9534a79849" }, + { "fontloader-l-math.lua", "5a0740b359e2d55d6dca06a23e3c9df6" }, { "fontloader-l-boolean.lua", "abe28515dd33e8f6c416c09bca351cf8" }, - { "fontloader-l-file.lua", "352b42b8360ada02ceb26a41f0bd2a76" }, - { "fontloader-l-io.lua", "bbcc4824d0ded30b415d7d2d5763028a" }, + { "fontloader-l-file.lua", "5839d88d4de96b9e9c7e7f8f8eb885b3" }, + { "fontloader-l-io.lua", "3396a8644bae37e49b2cfc0c388c78f8" }, { "fontloader-l-table.lua", "0330da8b3a08d39876def70df8d9c0e5" }, { "fontloader-l-string.lua", "9457f2af85723e0e6c46d0b5122e9000" }, { "fontloader-l-function.lua", "a7e68a9703c35238729da41a474e951b" }, - { "fontloader-l-lpeg.lua", "446dc07c8f6d2884122679fb4c8589e2" }, + { "fontloader-l-lpeg.lua", "8315a56e6bc7b2ede3f70c49bf2b5af5" }, { "fontloader-l-lua.lua", "420bc442e6d81f4624728a6c56fcd175" }, { "fontloader-fonts-gbn.lua", "34e4543a02f6fbc8c2ade896cb6dc7df" }, { "fontloader-fonts-lig.lua", "fbd251eea3810a43a8d5542319361d68" }, { "fontloader-fonts-ext.lua", "32013cbc5d5d336be8b1d1e5879d86c4" }, { "fontloader-fonts-def.lua", "f435e0875f203f343157baeff876ec9c" }, { "fontloader-fonts-tfm.lua", "f0eb5e2a8068b17ad401bb7efdba1630" }, - { "fontloader-fonts-enc.lua", "5ff4ca50493d7c4ecea0e15c203099f0" }, + { "fontloader-fonts-enc.lua", "c24fd6b4f34f87b9c4ff5e6193ce2656" }, { "fontloader-fonts-mis.lua", "17e967c9ec4d001deefd43ddf25e98f7" }, - { "fontloader-basics-chr.lua", "64fafe4eaf55d64aead6513c1e74a024" }, - { "fontloader-basics-nod.lua", "b2e0a0e5e1e27349aed464fa738b8db1" }, + { "fontloader-basics-chr.lua", "ce4afac7ba19471f3c8f90800839f315" }, + { "fontloader-basics-nod.lua", "1750af43148def9e7a75c6900a96f9dc" }, { "fontloader-font-imp-effects.lua", "d05997c1826355548ec9aec1346f9f23" }, { "fontloader-font-imp-italics.lua", "defcb415bc1e0a0999b27773902adc18" }, { "fontloader-font-imp-ligatures.lua", "26ffcf089391445f7af59536c8814364" }, { "fontloader-font-imp-tex.lua", "ceb087ef6fa2f89aed7179f60ddf8f35" }, - { "fontloader-font-shp.lua", "2bb36adb4b264a895a2c4d97e1ac1d1e" }, + { "fontloader-font-shp.lua", "a40862fe382d26c1f3753431254bcfdd" }, { "fontloader-font-def.lua", "b9d908c2338b7dc443ce9d1e9fcb7139" }, { "fontloader-font-lua.lua", "1fbfdf7b689b2bdfd0e3bb9bf74ce136" }, { "fontloader-font-afk.lua", "9da14e0fb22129c053acc599d1312544" }, { "fontloader-font-one.lua", "bd39d42055a44ca2002f8ddbbbbf27c3" }, { "fontloader-font-onr.lua", "6d161533f3437435e12a0ad64866df44" }, { "fontloader-font-ocl.lua", "2a377351ae23c339764ec8ce2e8e9164" }, - { "fontloader-font-osd.lua", "0e051ca0d3b1a8e0604ced27e753fde8" }, - { "fontloader-font-otc.lua", "0f12230ea07b5151f75d52726977e91f" }, - { "fontloader-font-ots.lua", "cb106ae882e8548051f00aac8b7fa862" }, + { "fontloader-font-osd.lua", "a67b6fb465e7a8b9ecf7c6bbf8dfda03" }, + { "fontloader-font-otc.lua", "582295d3d1e44cd4a31e8bbc1156f7c1" }, + { "fontloader-font-ots.lua", "55316a605d11a1d07d38795224c90a63" }, { "fontloader-font-ota.lua", "2bb12d708e35bc05443eba791e2554e7" }, - { "fontloader-font-oup.lua", "4eb2f26e51835239e26a8df2ecf04366" }, + { "fontloader-font-oup.lua", "e6e2cc0a202aec2c787d5524e64d340a" }, { "fontloader-font-otj.lua", "b2c165bd6771a57633f992d07384b739" }, - { "fontloader-font-oto.lua", "2b1d6bc63b2ebf8f2655a15e1cda6541" }, - { "fontloader-font-otl.lua", "6f701653de713e8aa80d82c7cdf32d75" }, + { "fontloader-font-oto.lua", "9febc4d75d17a543cd73fb17918f7307" }, + { "fontloader-font-otl.lua", "67078f58bc1a612e4646523538a4e3eb" }, { "fontloader-font-ott.lua", "3f67b1b1d40bbc222681d8db4aff0f42" }, { "fontloader-font-oti.lua", "309a75f9c14b77d87e94eba827dc4e71" }, - { "fontloader-font-dsp.lua", "174ce11f3ba0bddf270f48d8d2addd56" }, - { "fontloader-font-ttf.lua", "ffffc827e1bcddc33a2b615340ecff7f" }, - { "fontloader-font-cff.lua", "01e7c6fbc8961a566f4e4f8cbedb7004" }, - { "fontloader-font-otr.lua", "ad238624c9ebd488d4bbe9af3b620189" }, + { "fontloader-font-dsp.lua", "7616dae672693fd8ec0f1892358019a2" }, + { "fontloader-font-ttf.lua", "feba1fdc80160343b24f89ef978e53cb" }, + { "fontloader-font-cff.lua", "e38c1a40e1d1d577cdf7920fa50d0654" }, + { "fontloader-font-otr.lua", "91f70c1216e60227e1437b6c8dcd8647" }, { "fontloader-font-vfc.lua", "a81c29eda98cd62cbebdb6c93544b50d" }, { "fontloader-font-map.lua", "c4a39db5ad822b348aa61bca5f4a599a" }, { "fontloader-font-cid.lua", "22b0367742fb253deef84ef7ccf5e8de" }, { "fontloader-font-con.lua", "724e5dd14aec2d4d189f8619898004cf" }, { "fontloader-font-ini.lua", "5343de60613adb24cdd7a8d2ee1d153c" }, { "fontloader-data-con.lua", "23a0f5386d46a1c157ae82a373e35fa0" }, - { "fontloader-reference.lua", "29911e74397766ee36205736166dd763" }, - { "fontloader-basics-gen.lua", "4bc43036e9796fccb0b8869f4b05bf2e" }, + { "fontloader-reference.lua", "066680a4fe38fad14eb27f56d4616104" }, + { "fontloader-basics-gen.lua", "e470c13b1ed41e38932e93518219ad74" }, { "mktests", "918cb50be9ee8bd645ac1a27dc501e8c" }, - { "mkstatus", "7f2fd945c1247f6af6b96c4ed49ddeab" }, + { "mkstatus", "3a71ea95e4daeac77ead843edb0ce283" }, { "mkcharacters", "bfca0e96e1e351afe7077502f9595af9" }, { "mkglyphlist", "7a8a8fdc0eac7fac2f13ec8f4e8e7aa9" }, { "mkimport", "7d62f1042e548f8b9368f26b9ba4be5c" }, - { "luaotfload-glyphlist.lua", "54908a38bf531914f350614afff2ccf6" }, + { "luaotfload-glyphlist.lua", "dc0ed15ca157aec56a509819d4c24046" }, { "luaotfload-characters.lua", "76415ca3ede312cad6e09038323489ae" }, - { "luaotfload-dvi.lua", "403603ff9e267d2b9ac631e63b869718" }, - { "luaotfload-tounicode.lua", "8f7850323417188eef5197d4e420bd34" }, - { "luaotfload-unicode.lua", "5bc464447c3f5b154d8368904044d654" }, - { "luaotfload-resolvers.lua", "3655371360dd87b4a7c83e355357e6eb" }, - { "luaotfload-parsers.lua", "c715cb6ea43d644ec26d6e87d25b5d27" }, - { "luaotfload-fallback.lua", "78a566243c0ad99e74f93fface672fb5" }, - { "luaotfload-szss.lua", "4c431d8f76e06200b2df3269615097cc" }, - { "luaotfload-scripts.lua", "12bf373853ead3eab3c83ec62e37bbd6" }, - { "luaotfload-multiscript.lua", "a46096b20f5442acea29c75b77f763a7" }, - { "luaotfload-loaders.lua", "ba59f0161e985f4c97c54245a74a8d29" }, - { "luaotfload-harf-plug.lua", "bded3b2beccab4943f616b3069cbb372" }, - { "luaotfload-harf-define.lua", "a5e760f71e08a8ff40a40defc9d59e4e" }, - { "luaotfload-notdef.lua", "dbb4718b4c1b46adc5870af3f5cc6cac" }, - { "luaotfload-embolden.lua", "7cc1563093c005c40c11b8c327e617f1" }, - { "luaotfload-letterspace.lua", "e7ee541a56954ebfa1a227aa59614bb9" }, - { "luaotfload-features.lua", "bf81e7630c3786ce12c1f9b61ef73037" }, - { "luaotfload-database.lua", "9b911605805ca1fab7d4088c0c50a5d2" }, - { "luaotfload-configuration.lua", "ddedd9c27f810b254547c6a799aba892" }, - { "luaotfload-colors.lua", "f4e86b8cbbcc790a64dae59b6b05b3d1" }, - { "luaotfload-auxiliary.lua", "e814d7c408817ae01a96f279c278ed08" }, - { "luaotfload-filelist.lua", "1108447545fbcb08aa0f6d586988c2e1" }, - { "luaotfload-tool.lua", "4ca593bc9413e4de889f1c0df6e48219" }, - { "luaotfload-diagnostics.lua", "4fa11f02259f2f9e7cd60d4b668b6a33" }, - { "luaotfload-log.lua", "e1af43403d8bf68d95276c0dc00e64f0" }, - { "luaotfload-init.lua", "f80c78b2296664ba42514674d104d093" }, + { "luaotfload-dvi.lua", "f4e4d6cc443f477820521abcd87ef3f8" }, + { "luaotfload-tounicode.lua", "79795ade0ccdb99cd5e6cf989770db69" }, + { "luaotfload-unicode.lua", "68904dd143491dc0a4a1c0a063a118f9" }, + { "luaotfload-resolvers.lua", "1e844bc5d9b3d207f8914a11254afd67" }, + { "luaotfload-parsers.lua", "b0c087c6dbf75939d7be9523b7a0ca34" }, + { "luaotfload-fallback.lua", "88ed544bc0429f94606f72dace5704f0" }, + { "luaotfload-szss.lua", "7266e9d2122fb0785b595f9605627640" }, + { "luaotfload-scripts.lua", "c6c11811531c7e42194304d65bed7877" }, + { "luaotfload-multiscript.lua", "f9d7bedf75629d83f8d99397d15fdf86" }, + { "luaotfload-loaders.lua", "b2ef32297527eec59131de3b56f4d846" }, + { "luaotfload-harf-plug.lua", "7f3739c1b32de1f8e76275c317f5ed9a" }, + { "luaotfload-harf-define.lua", "1c2ce18281caede23d93c8b825df7239" }, + { "luaotfload-notdef.lua", "8b448aed817a764b675d1ead6bf932a7" }, + { "luaotfload-embolden.lua", "00ae296b36724c4f8dd4da62051039b2" }, + { "luaotfload-letterspace.lua", "f98fbda3ea174058b515b9234dfba495" }, + { "luaotfload-features.lua", "5c0d7d66953c7ecdd2a610d758d70f0d" }, + { "luaotfload-database.lua", "2ac2d6e5960257a7c5688c931bb91924" }, + { "luaotfload-configuration.lua", "05e6c5032b9ac6f05230d06c866161d6" }, + { "luaotfload-colors.lua", "3e1cd496c920d0e8296a9e99357f27eb" }, + { "luaotfload-auxiliary.lua", "32c6b48ab66a2ddf2a7d3063529e85f8" }, + { "luaotfload-filelist.lua", "b80ed418d9d297370182e9fcc9e497e5" }, + { "luaotfload-tool.lua", "2a6a1c510e44c4432724657259b12cbf" }, + { "luaotfload-diagnostics.lua", "e831068d26e0266cd081de8585c3cbc9" }, + { "luaotfload-log.lua", "889508edccd01035ecbeafdeeff60e35" }, + { "luaotfload-init.lua", "56ac12fac2e63d1a901463850062dd58" }, { "luaotfload-main.lua", "b19c9cc34cf1d676c39f872cfb41aef6" }, }, ["notes"]={ ["committer"]="Marcel Fabian Krüger <tex@2krueger.de>", - ["description"]="v3.18/2021-05-21", - ["loader"]="fontloader-2021-05-20.lua", - ["revision"]="3f2cacb9d2136142cffb183774c0edaeb4ea3314", - ["timestamp"]="2021-05-21 02:12:47 +0200", + ["description"]="v3.19/2022-02-24", + ["loader"]="fontloader-2022-02-24.lua", + ["revision"]="ef3a55e0da3709e95551c62376f31149771dcc18", + ["timestamp"]="2022-02-24 14:16:28 +0100", }, }
\ No newline at end of file diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-suppress.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-suppress.lua index 8ea049ba916..e0f3989c22a 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-suppress.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-suppress.lua @@ -5,8 +5,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-suppress", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / suppress", license = "GPL v2.0", author = "Marcel Krüger" diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-szss.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-szss.lua index 26c89eafa26..dcd8eacf65b 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-szss.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-szss.lua @@ -5,8 +5,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-szss", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / Special feature handling", license = "GPL v2.0", author = "Marcel Krüger" diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-t3.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-t3.lua new file mode 100644 index 00000000000..2588346724f --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-t3.lua @@ -0,0 +1,142 @@ +----------------------------------------------------------------------- +-- FILE: luaotfload-t3.lua +-- DESCRIPTION: part of luaotfload / Type 3 +----------------------------------------------------------------------- +do -- block to avoid to many local variables error + assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { + name = "luaotfload-t3", + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE + description = "luaotfload submodule / Type 3 font handling", + license = "GPL v2.0", + author = "Marcel Krüger", + copyright = "Luaotfload Development Team", + } +end + +--[[ +local function u8_to_utf16be(s) + local d = {} + local i = 0 + for _, cp, cp2 in utf8.codes(s) do + i = i + 1 + if cp > 0x10000 then + cp = cp - 0x10000 + local high = (cp>>10) | 0xD800 + local low = (cp&0x3FF) | 0xDC00 + d[i] = string.format('%04X%04X', high, low) + else + d[i] = string.format('%04X', cp) + end + end + return table.concat(d, '', 1, i) +end +]] + +local function t3factory(basename, designsize, callback) + local count = 0 + local gid_to_t3 = {} + local t3_to_gid = {} + return function(size) + local fids = {} + return function(gid, width, unicode) + local t3id = gid_to_t3[gid] + if not t3id then + t3id = count + count = count + 1 + gid_to_t3[gid] = t3id + t3_to_gid[t3id] = gid + end + local t3fid, t3cid = t3id >> 8, ~(t3id & 0xFF) + local fid = fids[t3fid + 1] + local fontdir = fid and font.getfont(fid) + local characters = fontdir and fontdir.characters + if not (characters and characters[t3cid]) then + characters = characters or {} + -- First create the character + local char = { + width = width, + height = 0, -- We never look at these two anyway + depth = 0, + tounicode = unicode, + } + characters[t3cid] = char + if fid then -- Font already exists + font.addcharacters(fid, { + characters = { + [t3cid] = char, + }, + }) + else + fontdir = { + name = basename .. '_' .. basesize .. '_' .. t3fid, + format = 'type3', + psname = 'none', + filename = 'not_a_real_filename', -- Can't be null to ensure reuse + fullname = basename .. basesize .. '_' .. t3fid, + characters = characters, + designsize = basesize, + size = size, + cidinfo = {}, -- Can't be null to ensure reuse + attributes = '/FontDescriptor<<\z + /Type/FontDescriptor\z + /FontName/VirtualLuaFont\z + /Flags 4\z + /ItalicAngle 0\z + >>', -- TODO: Needs work + t3_handler = function(stage, _, cid) + cid = cid and assert(t3_to_gid[(t3fid << 8) | ~cid]) + return callback(stage, cid) + end + } + fid = font.define(fontdir) + fids[t3fid + 1] = fid + end + end + vf.font(fid) + vf.char(t3cid) + end + end +end + +luatexbase.add_to_callback('provide_charproc_data', function(stage, fid, ...) + local fontdir = font.getfont(fid) + if not fontdir then error'Unknown font' end + local handler = fontdir.t3_handler + if not handler then error'Missing t3handler in Type 3 font' end + return handler(stage, fid, ...) +end, 'luaotfload.t3') + +return t3factory +--[===[ + function callback(stage, cid) + if stage == 1 then + local char = assert(characters[cid]) + local head = assert(char.head) + local resource = tex.saveboxresource(head, nil--[[attributes]], nil--[[resources]], true--[[immediate]], 0--[[mode]], nil--[[margin]]) + local name = string.format('/F%i', pdf.getxformname(resource)) + local resources = characters.resources + if resources then + characters.resources = string.format('%s %s %i 0 R', resources, name, resource) + else + characters.resources = string.format('%s %i 0 R', name, resource) + end + char.resource = name + elseif stage == 2 then + local char = assert(characters[cid]) + width = char.width/65781 + return pdf.obj{ + type = 'stream', + immediate = true, + -- objnum = + -- attr = + -- compresslevel = + string = string.format('%g 0 d0 %s Do', width, char.resource) + }, width + elseif stage == 3 then + return 65781/655360, string.format('/XObject<<%s>>', characters.resources) + end + print(stage, fid, cid) + end, + } +]===] diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-tounicode.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-tounicode.lua index 928dfd30fc1..09ca40adbe4 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-tounicode.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-tounicode.lua @@ -5,8 +5,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-tounicode", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / tounicode", license = "GPL v2.0", author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang, Marcel Krüger", diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-unicode.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-unicode.lua index ccc3988ba1d..25a7b2046eb 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-unicode.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-unicode.lua @@ -5,8 +5,8 @@ assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-unicode", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / Unicode helpers", license = "CC0 1.0 Universal", author = "Marcel Krüger" diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua index 8d769d7dc8c..f21ded5ae62 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua @@ -20,8 +20,8 @@ local authors = "\z -- version number is used below! local ProvidesLuaModule = { name = "luaotfload", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "Lua based OpenType font support", author = authors, copyright = authors, @@ -172,11 +172,6 @@ local function make_loader_name (prefix, name) return prefix .. "-" .. name .. ".lua" end -local timing_info = { - t_load = { }, - t_init = { }, -} - local function make_loader (prefix, load_helper) return function (name) local t_0 = osgettimeofday () @@ -184,7 +179,6 @@ local function make_loader (prefix, load_helper) --- We don’t want the stack info from inside, so just pcall(). local ok, data = pcall (load_helper or require, modname) local t_end = osgettimeofday () - timing_info.t_load [name] = t_end - t_0 if not ok then io.write "\n" local msg = luaotfload.log and luaotfload.log.report or print @@ -256,7 +250,6 @@ local function context_loader (name, path) end local ret = context_isolated_load (modpath) local t_end = osgettimeofday () - timing_info.t_load [name] = t_end - t_0 if ret ~= nil then --- require () returns “true” upon success unless the loaded file @@ -293,7 +286,6 @@ local function install_loaders () logreport ("log", 4, "load", "Module %q loaded in %g ms.", name, d_t * 1000) - timing_info.t_init [name] = d_t end end @@ -368,7 +360,6 @@ luaotfload.main = function () logreport ("log", 1, "main", "initialization completed in %0.3f seconds\n", osgettimeofday() - starttime) -----inspect (timing_info) luaotfload_module = nil end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty index 20dc5ffb4fb..7dbccb1f8c4 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty @@ -39,7 +39,7 @@ \fi \ifdefined\ProvidesPackage \ProvidesPackage{luaotfload}% - [2021/05/21 v3.18 OpenType layout system] + [2022/02/24 v3.19 OpenType layout system] \fi \directlua{ if xpcall(function ()% |