From mboxrd@z Thu Jan 1 00:00:00 1970 X-Msuck: nntp://news.gmane.io/gmane.comp.tex.context/108520 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Marcel Fabian =?utf-8?Q?Kr=C3=BCger?= Newsgroups: gmane.comp.tex.context Subject: CFF2 based variable fonts with SubRS Date: Wed, 26 Aug 2020 11:01:37 +0200 Message-ID: <20200826090137.oomejyu6us4kjvhd@yoga> Reply-To: mailing list for ConTeXt users Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="3wyd2dxbum3tukak" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="38437"; mail-complaints-to="usenet@ciao.gmane.io" To: ConTeXt users list Original-X-From: ntg-context-bounces@ntg.nl Wed Aug 26 11:02:03 2020 Return-path: Envelope-to: gctc-ntg-context-518@m.gmane-mx.org Original-Received: from zapf.boekplan.nl ([5.39.185.232] helo=zapf.ntg.nl) by ciao.gmane.io with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kArJj-0009tC-5s for gctc-ntg-context-518@m.gmane-mx.org; Wed, 26 Aug 2020 11:02:03 +0200 Original-Received: from localhost (localhost [127.0.0.1]) by zapf.ntg.nl (Postfix) with ESMTP id 4CB0C1A86E4; Wed, 26 Aug 2020 11:01:43 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at zapf.boekplan.nl Original-Received: from zapf.ntg.nl ([127.0.0.1]) by localhost (zapf.ntg.nl [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Y8dyTGGC_4uA; Wed, 26 Aug 2020 11:01:42 +0200 (CEST) Original-Received: from zapf.ntg.nl (localhost [127.0.0.1]) by zapf.ntg.nl (Postfix) with ESMTP id 3CA8B1A86F1; Wed, 26 Aug 2020 11:01:42 +0200 (CEST) Original-Received: from localhost (localhost [127.0.0.1]) by zapf.ntg.nl (Postfix) with ESMTP id C6A1C1A86E4 for ; Wed, 26 Aug 2020 11:01:40 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at zapf.boekplan.nl Original-Received: from zapf.ntg.nl ([127.0.0.1]) by localhost (zapf.ntg.nl [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id r5kXMZkaTir2 for ; Wed, 26 Aug 2020 11:01:39 +0200 (CEST) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=46.41.2.83; helo=smtp.maths.network; envelope-from=tex@2krueger.de; receiver= Original-Received: from smtp.maths.network (smtp.maths.network [46.41.2.83]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by zapf.ntg.nl (Postfix) with ESMTPS id A0A561A86BE for ; Wed, 26 Aug 2020 11:01:39 +0200 (CEST) X-Delivered-To: ntg-context@ntg.nl DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=2krueger.de; s=locserv; t=1598432498; bh=ene2S/YHUFRG/2NKxiuWnqptHgFjzXImBrFr9grJjGI=; h=Date:From:To:Subject; b=PxJAdludKm4JQlXWJdW9BE8QrsfwgL7MLPAwIAFw4a9tgjCWcBVLCzRY4uhwWpF78 MW5aPYSzAMsdyb1h7dhvHKjx5hD4uOcBakfOW6SAAPPPLFXlsIKVJPi5g1XTb1eEfC ScL/FPrO/zmnA9B4PYTWbAFDaoK6RFeeH7+pJGSg= Original-Received: from yoga (unknown [192.168.178.227]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp.maths.network (Postfix) with ESMTPSA id 8E4A01EE8AC0 for ; Wed, 26 Aug 2020 11:01:38 +0200 (CEST) Content-Disposition: inline X-BeenThere: ntg-context@ntg.nl X-Mailman-Version: 2.1.26 Precedence: list List-Id: mailing list for ConTeXt users List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ntg-context-bounces@ntg.nl Original-Sender: "ntg-context" Xref: news.gmane.io gmane.comp.tex.context:108520 Archived-At: --3wyd2dxbum3tukak Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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 --3wyd2dxbum3tukak Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="cff2_patch.diff" 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 --3wyd2dxbum3tukak Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Content-Disposition: inline X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f X19fX19fX19fX19fX19fX19fX19fX19fX18KSWYgeW91ciBxdWVzdGlvbiBpcyBvZiBpbnRlcmVz dCB0byBvdGhlcnMgYXMgd2VsbCwgcGxlYXNlIGFkZCBhbiBlbnRyeSB0byB0aGUgV2lraSEKCm1h aWxsaXN0IDogbnRnLWNvbnRleHRAbnRnLm5sIC8gaHR0cDovL3d3dy5udGcubmwvbWFpbG1hbi9s aXN0aW5mby9udGctY29udGV4dAp3ZWJwYWdlICA6IGh0dHA6Ly93d3cucHJhZ21hLWFkZS5ubCAv IGh0dHA6Ly9jb250ZXh0LmFhbmhldC5uZXQKYXJjaGl2ZSAgOiBodHRwczovL2JpdGJ1Y2tldC5v cmcvcGhnL2NvbnRleHQtbWlycm9yL2NvbW1pdHMvCndpa2kgICAgIDogaHR0cDovL2NvbnRleHRn YXJkZW4ubmV0Cl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fCg== --3wyd2dxbum3tukak--