modifying kerning breaks opentype ligatures (and other features)
Hello list, i noticed that setting a different kerning breaks some features of opentype fonts; that is true, for example, for ligatures and fractions (frac feature). It looks like a modified kerning inserts something between two adjacent character, making them no more adjacent. That way the lookup of opentype tables fails. "f", "l" => "fl" ligature glyph "f", [kerning], "l" => distinct "f" and "l" glyphs "1", "/", "4" => "¼" glyph (with "frac" feature switched on) "1", [kerning], "/", [kerning], "4" => distinct "1", "/", "4" glyphs (always with "frac" feature switched on) MWE: \definecharacterkerning[narrow][factor=-.015] \definecharacterkerning[wide][factor=.015] \definefontfeature[frac][frac=yes] \starttext \feature[+][frac]% Some ligatures: float, finance, affine; a fraction: 1/4. \setcharacterkerning[narrow]Some ligatures: float, finance, affine; a fraction: 1/4. \setcharacterkerning[wide]Some ligatures: float, finance, affine; a fraction: 1/4. \stoptext In my XML to PDF workflow, fractions are marked up, so it's easy to switch the kerning off only for them. Instead i have no solution for ligatures: parts of text with a modified kerning loose ligatures, and i end up with the same words, once with ligatures, once without, even in the same page. Is there a workaround for this? Massimiliano
An example of local correction, but it's not automatic: \definecharacterkerning[narrow][factor=-.02] \definecharacterkerning[wide][factor=.02] \definefontfeature[frac][frac=yes] \def\Narrow#1{\bgroup\setcharacterkerning[narrow]#1\egroup} \def\Wide#1{\bgroup\setcharacterkerning[wide]#1\egroup} \def\NoKerning#1{\bgroup\resetcharacterkerning #1\egroup} \starttext \feature[+][frac]% Some ligatures: float, finance, affine; a fraction: 1/4. \blank Modified kerning: \Narrow{Some ligatures: float, finance, affine; a fraction: 1/4.} \Wide{Some ligatures: float, finance, affine; a fraction: 1/4.} \blank Same as above, but locally corrected: \Narrow{Some ligatures: \NoKerning{fl}oat, \NoKerning{fi}nance, a\NoKerning{ffi}ne; a fraction: \NoKerning{1/4}.} \Wide{Some ligatures: \NoKerning{fl}oat, \NoKerning{fi}nance, a\NoKerning{ffi}ne; a fraction: \NoKerning{1/4}.} \stoptext PS: (errata corrige)
"1", "/", "4" => "¼" glyph (with "frac" feature switched on)
"1", "/", "4" => "smaller 1" glyph moved up, fraction line glyph, "smaller 4" glyph moved left and down
On 12/10/2018 4:26 PM, mf wrote:
Hello list, i noticed that setting a different kerning breaks some features of opentype fonts; that is true, for example, for ligatures and fractions (frac feature).
It looks like a modified kerning inserts something between two adjacent character, making them no more adjacent. That way the lookup of opentype tables fails.
"f", "l" => "fl" ligature glyph
"f", [kerning], "l" => distinct "f" and "l" glyphs
"1", "/", "4" => "¼" glyph (with "frac" feature switched on)
"1", [kerning], "/", [kerning], "4" => distinct "1", "/", "4" glyphs (always with "frac" feature switched on)
MWE:
\definecharacterkerning[narrow][factor=-.015] \definecharacterkerning[wide][factor=.015]
\definefontfeature[frac][frac=yes]
\starttext \feature[+][frac]% Some ligatures: float, finance, affine; a fraction: 1/4.
\setcharacterkerning[narrow]Some ligatures: float, finance, affine; a fraction: 1/4.
\setcharacterkerning[wide]Some ligatures: float, finance, affine; a fraction: 1/4.
\stoptext
In my XML to PDF workflow, fractions are marked up, so it's easy to switch the kerning off only for them.
Instead i have no solution for ligatures: parts of text with a modified kerning loose ligatures, and i end up with the same words, once with ligatures, once without, even in the same page. Is there a workaround for this? character kerning is bad anyway ... say that we have:
effe and this is e ff e you definitely don't want to kern like e kern ff kern ff but e kern f kern f kern e so, ligatures are pretty useless with intercharacter kerning (as per definecharacterning) ... just conceptually incomatible (it is possible to turn it on but it really isnot what you want) ----------------------------------------------------------------- Hans Hagen | PRAGMA ADE Ridderstraat 27 | 8061 GH Hasselt | The Netherlands tel: 038 477 53 69 | www.pragma-ade.nl | www.pragma-pod.nl -----------------------------------------------------------------
character kerning is bad anyway ...
I'm using it to get a paragraph one line shorter or longer, or to tune what in TeX is set with \parfillskip. I'm using ConTeXt with a mindset developed on other typesetting softwares. That's not right, i know, but for now i'm finding easier to work with character kerning than with \looseness, \parfillskip and \emergencystretch. \looseness=-1 quite never reduces the lines' number, because the inter-word spaces are already very well optimized. \looseness=1 quite always produces a short last line, so that i often work on \emergencystretch and \parfillskip to make it wider. But this way i have to work on 3 parameters; with kerning you have only one: uglier for ligatures but easier. I suspect TeX has better ways to deal with that problem, but i don't know them (hints are welcome).
effe
and this is
e ff e
you definitely don't want to kern like
e kern ff kern ff
but
e kern f kern f kern e
so, ligatures are pretty useless with intercharacter kerning (as per definecharacterning) ... just conceptually incomatible
(it is possible to turn it on but it really isnot what you want)
yes, the ligature would not get the same shrinkage or stretch of the rest, but i don't modify the kerning that much (factor=.02 is the maximum value). It's still acceptable. But i should consider all the text parts with a modified kerning and disable kerning around character sequences like "ff", "ffi", "fl" and so on. Luckily, the XHTML markup "knows" where the kerning is modified: <p class="wide">A paragraph with a modified kerning</p> should become <p class="wide">A paragraph with a modi<dk>fi</dk>ed kerning</p> Where <dk>...</dk> (dk=Disable Kerning) are tags to be inserted automatically before feeding the XHTML into ConTeXt. I'm not very happy or proud about it, but it can be done. Do you think it can be better done inside ConTeXt? Massimiliano
On 12/10/2018 6:16 PM, mf wrote:
yes, the ligature would not get the same shrinkage or stretch of the rest, but i don't modify the kerning that much (factor=.02 is the maximum value). It's still acceptable. ok, then don't use this characterkerning but use expansion (hz) ... you can do that extreme (larg evalues) if needed (after all, big publishers do that too)
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 -----------------------------------------------------------------
But i should consider all the text parts with a modified kerning and disable kerning around character sequences like "ff", "ffi", "fl" and so on. Luckily, the XHTML markup "knows" where the kerning is modified:
<p class="wide">A paragraph with a modified kerning</p>
should become
<p class="wide">A paragraph with a modi<dk>fi</dk>ed kerning</p>
Where <dk>...</dk> (dk=Disable Kerning) are tags to be inserted automatically before feeding the XHTML into ConTeXt. I'm not very happy or proud about it, but it can be done. Do you think it can be better done inside ConTeXt?
I nearly managed to do it in ConTeXt, but I can't create the "<dk>" elements right (the "new_dk_element" function is wrong): \startbuffer[test] <text> <p>Some ligatures: float, finance, affine, affluent.</p> <p>Modified kerning:</p> <p class="narrow">Some ligatures: float, finance, affine, affluent.</p> <p class="wide">Some ligatures: float, finance, affine, affluent.</p> </text> \stopbuffer \startluacode local function new_dk_element( parent, text ) return { tag = "dk", ns = "", rn = "", dt = { text }, at = {}, command = "xml:dk", __p__ = parent } --[[ local t = xml.toxml( "<dk>" .. text .. "</dk>" ) t.__p__ = parent return t ]]-- end local append = table.insert local function saveLigaturesFromKerning( t ) if t and t.dt then local dt = t.dt local t_copy = {} for k,v in pairs( t ) do if k ~= "dt" then t_copy[ k ] = v end end local new_dt = {} local i local child local b, e for i = 1, #dt, 1 do child = dt[ i ] if type( child ) == "string" then local s = child repeat b, e = string.find( s, "f?f[il]" ) if b then if b > 1 then append( new_dt, string.sub( s, 1, b - 1 ) ) end append( new_dt, new_dk_element( t, string.sub( s, b, e ) ) ) -- append( new_dt, "[" .. string.sub( s, b, e ) .. "]" ) s = string.sub( s, e + 1 ) else append( new_dt, s ) end until not b else append( new_dt, child ) end end t.dt = new_dt end return t end function xml.functions.textWithKerning( t ) local kt = saveLigaturesFromKerning( t ) lxml.flush( kt ) end \stopluacode \definecharacterkerning[narrow][factor=-.02] \definecharacterkerning[wide][factor=.02] \def\Narrow#1{\bgroup\setcharacterkerning[narrow]#1\egroup} \def\Wide#1{\bgroup\setcharacterkerning[wide]#1\egroup} \def\NoKerning#1{\bgroup\resetcharacterkerning #1\egroup} \startxmlsetups xml:test \xmlsetsetup{#1}{text|p|dk}{xml:*} \xmlsetsetup{#1}{{p.narrow}}{xml:p:narrow} \xmlsetsetup{#1}{{p.wide}}{xml:p:wide} \stopxmlsetups \xmlregistersetup{xml:test} \startxmlsetups xml:text \xmlflush{#1} \stopxmlsetups \startxmlsetups xml:p \xmlflush{#1}\par \stopxmlsetups \startxmlsetups xml:p:narrow \Narrow{\xmlfunction{#1}{textWithKerning}}\par \stopxmlsetups \startxmlsetups xml:p:wide \Wide{\xmlfunction{#1}{textWithKerning}}\par \stopxmlsetups \startxmlsetups xml:dk \NoKerning{\red\xmlflush{#1}} \stopxmlsetups \starttext \xmlprocessbuffer{xml:test}{test}{} \stoptext
On 12/11/2018 11:34 AM, mf wrote:
But i should consider all the text parts with a modified kerning and disable kerning around character sequences like "ff", "ffi", "fl" and so on. Luckily, the XHTML markup "knows" where the kerning is modified:
<p class="wide">A paragraph with a modified kerning</p>
should become
<p class="wide">A paragraph with a modi<dk>fi</dk>ed kerning</p>
Where <dk>...</dk> (dk=Disable Kerning) are tags to be inserted automatically before feeding the XHTML into ConTeXt. I'm not very happy or proud about it, but it can be done. Do you think it can be better done inside ConTeXt?
I nearly managed to do it in ConTeXt, but I can't create the "<dk>" elements right (the "new_dk_element" function is wrong):
did you play with \definefontfeature[whatever][keepligatures=auto] \definecharacterkerning [extrakerning] [factor=0.125,features=whatever] \starttext \setcharacterkerning[extrakerning] effe fietsen \stoptext
\startbuffer[test] <text> <p>Some ligatures: float, finance, affine, affluent.</p> <p>Modified kerning:</p> <p class="narrow">Some ligatures: float, finance, affine, affluent.</p> <p class="wide">Some ligatures: float, finance, affine, affluent.</p> </text> \stopbuffer
\startluacode local function new_dk_element( parent, text ) return { tag = "dk", ns = "", rn = "", dt = { text }, at = {}, command = "xml:dk", __p__ = parent } --[[ local t = xml.toxml( "<dk>" .. text .. "</dk>" ) t.__p__ = parent return t ]]-- end
local append = table.insert local function saveLigaturesFromKerning( t ) if t and t.dt then local dt = t.dt local t_copy = {} for k,v in pairs( t ) do if k ~= "dt" then t_copy[ k ] = v end end local new_dt = {} local i local child local b, e for i = 1, #dt, 1 do child = dt[ i ] if type( child ) == "string" then local s = child repeat b, e = string.find( s, "f?f[il]" ) if b then if b > 1 then append( new_dt, string.sub( s, 1, b - 1 ) ) end append( new_dt, new_dk_element( t, string.sub( s, b, e ) ) ) -- append( new_dt, "[" .. string.sub( s, b, e ) .. "]" ) s = string.sub( s, e + 1 ) else append( new_dt, s ) end until not b else append( new_dt, child ) end end t.dt = new_dt end return t end
function xml.functions.textWithKerning( t ) local kt = saveLigaturesFromKerning( t ) lxml.flush( kt ) end \stopluacode
\definecharacterkerning[narrow][factor=-.02] \definecharacterkerning[wide][factor=.02] \def\Narrow#1{\bgroup\setcharacterkerning[narrow]#1\egroup} \def\Wide#1{\bgroup\setcharacterkerning[wide]#1\egroup} \def\NoKerning#1{\bgroup\resetcharacterkerning #1\egroup}
\startxmlsetups xml:test \xmlsetsetup{#1}{text|p|dk}{xml:*} \xmlsetsetup{#1}{{p.narrow}}{xml:p:narrow} \xmlsetsetup{#1}{{p.wide}}{xml:p:wide} \stopxmlsetups
\xmlregistersetup{xml:test}
\startxmlsetups xml:text \xmlflush{#1} \stopxmlsetups
\startxmlsetups xml:p \xmlflush{#1}\par \stopxmlsetups
\startxmlsetups xml:p:narrow \Narrow{\xmlfunction{#1}{textWithKerning}}\par \stopxmlsetups
\startxmlsetups xml:p:wide \Wide{\xmlfunction{#1}{textWithKerning}}\par \stopxmlsetups
\startxmlsetups xml:dk \NoKerning{\red\xmlflush{#1}} \stopxmlsetups
\starttext \xmlprocessbuffer{xml:test}{test}{} \stoptext
___________________________________________________________________________________
If your question is of interest to others as well, please add an entry to the Wiki!
maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context webpage : http://www.pragma-ade.nl / http://context.aanhet.net archive : https://bitbucket.org/phg/context-mirror/commits/ wiki : http://contextgarden.net ___________________________________________________________________________________
-- ----------------------------------------------------------------- Hans Hagen | PRAGMA ADE Ridderstraat 27 | 8061 GH Hasselt | The Netherlands tel: 038 477 53 69 | www.pragma-ade.nl | www.pragma-pod.nl -----------------------------------------------------------------
did you play with
\definefontfeature[whatever][keepligatures=auto]
\definecharacterkerning [extrakerning] [factor=0.125,features=whatever]
\starttext \setcharacterkerning[extrakerning] effe fietsen \stoptext
Great! Thanks! This now works: \definefontfeature[frac][frac=yes] \definefontfeature[klig][keepligatures=auto] \definecharacterkerning[narrow][factor=-.02,features=klig] \definecharacterkerning[wide][factor=.02,features=klig] \def\Narrow#1{\dontleavehmode\bgroup\setcharacterkerning[narrow]#1\egroup} \def\Wide#1{\dontleavehmode\bgroup\setcharacterkerning[wide]#1\egroup} \def\NoKerning#1{\dontleavehmode\bgroup\resetcharacterkerning #1\egroup} \starttext \feature[+][frac]% Some ligatures: float, finance, affine; a fraction: 1/4. \blank Modified kerning: \Narrow{Some ligatures: float, finance, affine; a fraction: 1/4.} \Wide{Some ligatures: float, finance, affine; a fraction: 1/4.} \stoptext BTW, back to the automatic insertion of tags, how can i create children elements? In other words, how should i rewrite new_dk_element? The problem is this: - you have an element t and t.dt = { "float, finance, affine, affluent" } - t.dt should become = { DK1, "oat", DK2, "nance, a", DK3, "ne, a", DK4, "uent" } - where DK1 ... DK4 are children elements of t with tg="dk" and dt respectively equal to { "fl" }, { "fi" }, { "ffi" }, { "ffl" } My new_dk_element function should create such DKn elements, but they are not created right, because they look ignored by \xmlflush.
\startbuffer[test] <text> <p>Some ligatures: float, finance, affine, affluent.</p> <p>Modified kerning:</p> <p class="narrow">Some ligatures: float, finance, affine, affluent.</p> <p class="wide">Some ligatures: float, finance, affine, affluent.</p> </text> \stopbuffer
\startluacode local function new_dk_element( parent, text ) return { tag = "dk", ns = "", rn = "", dt = { text }, at = {}, command = "xml:dk", __p__ = parent } --[[ local t = xml.toxml( "<dk>" .. text .. "</dk>" ) t.__p__ = parent return t ]]-- end
local append = table.insert local function saveLigaturesFromKerning( t ) if t and t.dt then local dt = t.dt local t_copy = {} for k,v in pairs( t ) do if k ~= "dt" then t_copy[ k ] = v end end local new_dt = {} local i local child local b, e for i = 1, #dt, 1 do child = dt[ i ] if type( child ) == "string" then local s = child repeat b, e = string.find( s, "f?f[il]" ) if b then if b > 1 then append( new_dt, string.sub( s, 1, b - 1 ) ) end append( new_dt, new_dk_element( t, string.sub( s, b, e ) ) ) -- append( new_dt, "[" .. string.sub( s, b, e ) .. "]" ) s = string.sub( s, e + 1 ) else append( new_dt, s ) end until not b else append( new_dt, child ) end end t.dt = new_dt end return t end
function xml.functions.textWithKerning( t ) local kt = saveLigaturesFromKerning( t ) lxml.flush( kt ) end \stopluacode
\definecharacterkerning[narrow][factor=-.02] \definecharacterkerning[wide][factor=.02] \def\Narrow#1{\bgroup\setcharacterkerning[narrow]#1\egroup} \def\Wide#1{\bgroup\setcharacterkerning[wide]#1\egroup} \def\NoKerning#1{\bgroup\resetcharacterkerning #1\egroup}
\startxmlsetups xml:test \xmlsetsetup{#1}{text|p|dk}{xml:*} \xmlsetsetup{#1}{{p.narrow}}{xml:p:narrow} \xmlsetsetup{#1}{{p.wide}}{xml:p:wide} \stopxmlsetups
\xmlregistersetup{xml:test}
\startxmlsetups xml:text \xmlflush{#1} \stopxmlsetups
\startxmlsetups xml:p \xmlflush{#1}\par \stopxmlsetups
\startxmlsetups xml:p:narrow \Narrow{\xmlfunction{#1}{textWithKerning}}\par \stopxmlsetups
\startxmlsetups xml:p:wide \Wide{\xmlfunction{#1}{textWithKerning}}\par \stopxmlsetups
\startxmlsetups xml:dk \NoKerning{\red\xmlflush{#1}} \stopxmlsetups
\starttext \xmlprocessbuffer{xml:test}{test}{} \stoptext
On 12/11/2018 1:45 PM, mf wrote: hm that is quit emessy yes? I found another solution (that we used once) that I can turn into a helper ... I'll send it off list
\startbuffer[test] <text> <p>Some ligatures: float, finance, affine, affluent.</p> <p>Modified kerning:</p> <p class="narrow">Some ligatures: float, finance, affine, affluent.</p> <p class="wide">Some ligatures: float, finance, affine, affluent.</p> </text> \stopbuffer
\startluacode -- a lot of lua code messing with xml
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 -----------------------------------------------------------------
On 12/11/2018 07:06, Hans Hagen wrote:
On 12/11/2018 11:34 AM, mf wrote:
But i should consider all the text parts with a modified kerning and disable kerning around character sequences like "ff", "ffi", "fl" and so on. Luckily, the XHTML markup "knows" where the kerning is modified:
<p class="wide">A paragraph with a modified kerning</p>
should become
<p class="wide">A paragraph with a modi<dk>fi</dk>ed kerning</p>
Where <dk>...</dk> (dk=Disable Kerning) are tags to be inserted automatically before feeding the XHTML into ConTeXt. I'm not very happy or proud about it, but it can be done. Do you think it can be better done inside ConTeXt?
I nearly managed to do it in ConTeXt, but I can't create the "<dk>" elements right (the "new_dk_element" function is wrong):
did you play with
\definefontfeature[whatever][keepligatures=auto]
\definecharacterkerning [extrakerning] [factor=0.125,features=whatever]
\starttext \setcharacterkerning[extrakerning] effe fietsen \stoptext
Nice! It recognizes the difference between ccmp and liga, so now we have (almost perfect) support for Sperrdruck! There is a ZWNJ in the following example (between t and z inZeitzone) that gets expanded, leaving an enlarged gap. \mainlanguage [deo] \definefontfeature[frak][ccmp=yes,liga=yes] \definefont [FrakXIX] [UnifrakturMaguntia19.ttf*frak] \definefontfeature[whatever][keepligatures=auto] \definecharacterkerning [extrakerning] [factor=0.25,features=whatever] \define\Test{{\FrakXIX ſitzen/beſchütze/Zeitzone}} \starttext \tex{setcharacterkerning}\par \Test\par {\setcharacterkerning[extrakerning]\Test}\par \Test\par \blank \tex{kerncharacters}\par \Test\par {\kerncharacters[.25]{\Test}\par \Test\par \stoptext -- Rik
On 12/11/2018 15:54, Rik Kabel wrote:
On 12/11/2018 07:06, Hans Hagen wrote:
On 12/11/2018 11:34 AM, mf wrote:
But i should consider all the text parts with a modified kerning and disable kerning around character sequences like "ff", "ffi", "fl" and so on. Luckily, the XHTML markup "knows" where the kerning is modified:
<p class="wide">A paragraph with a modified kerning</p>
should become
<p class="wide">A paragraph with a modi<dk>fi</dk>ed kerning</p>
Where <dk>...</dk> (dk=Disable Kerning) are tags to be inserted automatically before feeding the XHTML into ConTeXt. I'm not very happy or proud about it, but it can be done. Do you think it can be better done inside ConTeXt?
I nearly managed to do it in ConTeXt, but I can't create the "<dk>" elements right (the "new_dk_element" function is wrong):
did you play with
\definefontfeature[whatever][keepligatures=auto]
\definecharacterkerning [extrakerning] [factor=0.125,features=whatever]
\starttext \setcharacterkerning[extrakerning] effe fietsen \stoptext
Nice! It recognizes the difference between ccmp and liga, so now we have (almost perfect) support for Sperrdruck! There is a ZWNJ in the following example (between t and z inZeitzone) that gets expanded, leaving an enlarged gap.
And I spoke too soon (not unusual, I know). The ſi ligature is not broken up, but it should be. Only ck, ct, ch, and ſt and tz should be preserved. So it looks like manual intervention is still required. (There are a few LaTeX packages that handle this well, chief among the microtype.) -- Rik
participants (3)
-
Hans Hagen
-
mf
-
Rik Kabel