CFF2 based variable fonts with SubRS
Hi, I was playing with the variable font support of ConTeXt's font loader and noticed some issues. 1. While parsing the LocalSubRS and GlobalSubRS Indices, ConTeXt tries to use 16bit count fields as in CFF(1) instead of 32bit fields as in CFF2, reducing the number of found subroutines by a factor of 65536. Effectivly this suppressed all subroutines in the fonts I tested. 2. For fonts where the Top DICT does not contain an FDSelect entry, ConTeXt tries to read the Private dictionary offset from the Top dictionary as in name-keyed CFF1 fonts. This is not the expected behavior for CFF2, where the Top dictionary never contains the Privte offset. Instead, a missing FDSelect means that exactly one entry exists in FDArray which should be used for all CIDs. This caused problems when ConTeXt tried to find the Private directory in order to find local subroutines. A test font demonstrating both issues is "SourceCode Variable" https://github.com/adobe-fonts/source-code-pro/releases: The document \definefontfeature [light] [default] [axis={weight=200}] \definefont [sourcelight] [file:SourceCodeVariable-Roman.otf*light] \starttext \sourcelight Hallo \stoptext generates [...] otf reader > cff > unknown local call 14, case 2 : [] n=0 otf reader > cff > unknown local call 11, case 2 : [] n=0 otf reader > cff > unknown local call 138, case 2 : [] n=0 otf reader > cff > unknown local call 314, case 2 : [300 -12] n=2 otf reader > cff > unknown local call 607, case 2 : [-99 -66 99 174] n=4 otf reader > fatal error in file 'SourceCodeVariable-Roman.otf': ...e0dde776fb1556f32e/formats/luametatex/font-cff-macro.lua:1517: attempt to perform arithmetic on a nil value (local 'v') stack traceback: ...e0dde776fb1556f32e/formats/luametatex/font-otr-macro.lua:2339: in metamethod 'add' ...e0dde776fb1556f32e/formats/luametatex/font-cff-macro.lua:1517: in local 'a' ...e0dde776fb1556f32e/formats/luametatex/font-cff-macro.lua:1980: in upvalue 'process' [...] A possible fix for both issues would be diff --git a/tex/context/base/mkiv/font-cff.lua b/tex/context/base/mkiv/font-cff.lua index c2cf0e699..d00637b8e 100644 --- a/tex/context/base/mkiv/font-cff.lua +++ b/tex/context/base/mkiv/font-cff.lua @@ -2265,8 +2265,8 @@ do end -local function readglobals(f,data) - local routines = readlengths(f) +local function readglobals(f,data,version) + local routines = readlengths(f,version == "cff2") for i=1,#routines do routines[i] = readbytetable(f,routines[i]) end @@ -2324,14 +2324,14 @@ local function readprivates(f,data) end end -local function readlocals(f,data,dictionary) +local function readlocals(f,data,dictionary,version) local header = data.header local private = dictionary.private if private then local subroutineoffset = private.data.subroutines if subroutineoffset ~= 0 then setposition(f,header.offset+private.offset+subroutineoffset) - local subroutines = readlengths(f) + local subroutines = readlengths(f,version=="cff2") for i=1,#subroutines do subroutines[i] = readbytetable(f,subroutines[i]) end @@ -2394,7 +2394,7 @@ readers.parsecharstrings = parsecharstrings -- used in font-onr.lua (type 1) local function readnoselect(f,fontdata,data,glyphs,doshapes,version,streams) local dictionaries = data.dictionaries local dictionary = dictionaries[1] - readglobals(f,data) + readglobals(f,data,version) readcharstrings(f,data,version) if version == "cff2" then dictionary.charset = nil @@ -2402,9 +2402,19 @@ local function readnoselect(f,fontdata,data,glyphs,doshapes,version,streams) readencodings(f,data) readcharsets(f,data,dictionary) end + local cid = dictionary.cid + local fdarray = cid and cid.fdarray + if fdarray and not dictionary.private then + setposition(f,data.header.offset+fdarray) + local dictionaries = readlengths(f,version=="cff2") + assert(#dictionaries == 1) + dictionaries[1] = readstring(f,dictionaries[1]) + parsedictionaries(data,dictionaries) + dictionary.private = dictionaries[1].private + end readprivates(f,data) parseprivates(data,data.dictionaries) - readlocals(f,data,dictionary) + readlocals(f,data,dictionary,version) startparsing(fontdata,data,streams) parsecharstrings(fontdata,data,glyphs,doshapes,version,streams) stopparsing(fontdata,data) @@ -2416,7 +2426,7 @@ local function readfdselect(f,fontdata,data,glyphs,doshapes,version,streams) local dictionary = dictionaries[1] local cid = dictionary.cid local cidselect = cid and cid.fdselect - readglobals(f,data) + readglobals(f,data,version) readcharstrings(f,data,version) if version ~= "cff2" then readencodings(f,data) @@ -2462,7 +2472,7 @@ local function readfdselect(f,fontdata,data,glyphs,doshapes,version,streams) local cidarray = cid.fdarray if cidarray then setposition(f,header.offset+cidarray) - local dictionaries = readlengths(f) + local dictionaries = readlengths(f, version == "cff2") for i=1,#dictionaries do dictionaries[i] = readstring(f,dictionaries[i]) end @@ -2470,7 +2480,7 @@ local function readfdselect(f,fontdata,data,glyphs,doshapes,version,streams) cid.dictionaries = dictionaries readcidprivates(f,data) for i=1,#dictionaries do - readlocals(f,data,dictionaries[i]) + readlocals(f,data,dictionaries[i],version) end startparsing(fontdata,data,streams) for i=1,#charstrings do ---- Best regards, Marcel
On 8/26/2020 11:01 AM, Marcel Fabian Krüger wrote:
Hi,
I was playing with the variable font support of ConTeXt's font loader and noticed some issues.
1. While parsing the LocalSubRS and GlobalSubRS Indices, ConTeXt tries to use 16bit count fields as in CFF(1) instead of 32bit fields as in CFF2, reducing the number of found subroutines by a factor of 65536. Effectivly this suppressed all subroutines in the fonts I tested.
2. For fonts where the Top DICT does not contain an FDSelect entry, ConTeXt tries to read the Private dictionary offset from the Top dictionary as in name-keyed CFF1 fonts. This is not the expected behavior for CFF2, where the Top dictionary never contains the Privte offset. Instead, a missing FDSelect means that exactly one entry exists in FDArray which should be used for all CIDs. This caused problems when ConTeXt tried to find the Private directory in order to find local subroutines.
A test font demonstrating both issues is "SourceCode Variable" https://github.com/adobe-fonts/source-code-pro/releases:
The document
\definefontfeature [light] [default] [axis={weight=200}]
\definefont [sourcelight] [file:SourceCodeVariable-Roman.otf*light]
\starttext \sourcelight Hallo \stoptext
Thanks. I'll check it. (Reminds me to check another issue in a variable font that I observed recently, something with accuracy, but of could also be a border case in a viewer as acrobat views ok.) I would not be surprised of there are more issues because at the time I wrote that code there were hardly any valid variable fonts and the specs were just showing up. (Most of those variable fonts were showcases.) Hans ----------------------------------------------------------------- Hans Hagen | PRAGMA ADE Ridderstraat 27 | 8061 GH Hasselt | The Netherlands tel: 038 477 53 69 | www.pragma-ade.nl | www.pragma-pod.nl -----------------------------------------------------------------
participants (2)
-
Hans Hagen
-
Marcel Fabian Krüger