texlive[58928] Master/texmf-dist: luatexko (20apr21)

commits+karl at tug.org commits+karl at tug.org
Tue Apr 20 22:14:00 CEST 2021


Revision: 58928
          http://tug.org/svn/texlive?view=revision&revision=58928
Author:   karl
Date:     2021-04-20 22:14:00 +0200 (Tue, 20 Apr 2021)
Log Message:
-----------
luatexko (20apr21)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/luatex/luatexko/ChangeLog
    trunk/Master/texmf-dist/doc/luatex/luatexko/README
    trunk/Master/texmf-dist/doc/luatex/luatexko/luatexko-doc.pdf
    trunk/Master/texmf-dist/doc/luatex/luatexko/luatexko-doc.tex
    trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko-normalize.lua
    trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko-uhc2utf8.lua
    trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua
    trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko.sty

Modified: trunk/Master/texmf-dist/doc/luatex/luatexko/ChangeLog
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/luatexko/ChangeLog	2021-04-20 20:13:46 UTC (rev 58927)
+++ trunk/Master/texmf-dist/doc/luatex/luatexko/ChangeLog	2021-04-20 20:14:00 UTC (rev 58928)
@@ -1,3 +1,11 @@
+2021-04-20	Dohyun Kim <nomos at ktug org>
+
+	Version 3.1
+
+	* luatexko.lua: insert 1/4 quad after vertical colons;
+	cleverer behavior when protrusion option is given;
+	process_fonts() is now called by hyphenate callback
+
 2021-03-01	Dohyun Kim <nomos at ktug org>
 
 	Version 3.0

Modified: trunk/Master/texmf-dist/doc/luatex/luatexko/README
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/luatexko/README	2021-04-20 20:13:46 UTC (rev 58927)
+++ trunk/Master/texmf-dist/doc/luatex/luatexko/README	2021-04-20 20:14:00 UTC (rev 58928)
@@ -1,4 +1,4 @@
-LuaTeX-ko Package version 3.0 (2021/03/01)
+LuaTeX-ko Package version 3.1 (2021/04/20)
 ===========================================
 
 This is a Lua(La)TeX macro package that supports typesetting Korean

Modified: trunk/Master/texmf-dist/doc/luatex/luatexko/luatexko-doc.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/luatex/luatexko/luatexko-doc.tex
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/luatexko/luatexko-doc.tex	2021-04-20 20:13:46 UTC (rev 58927)
+++ trunk/Master/texmf-dist/doc/luatex/luatexko/luatexko-doc.tex	2021-04-20 20:14:00 UTC (rev 58928)
@@ -14,22 +14,31 @@
 \usepackage{luacolor}
 \usepackage[hangul]{luatexko}
 \directlua{
-  local gidoffset = 0x120000
-  local notoserifcjkkr_hb = {
-    [171]     = { 0.5, 0   }, % «
-    [187]     = { 0,   0.5 }, % »
-    [62253+gidoffset] = { 0.5, 0   }, % (
-    [62254+gidoffset] = { 0,   0.5 }, % )
-    [62255+gidoffset] = { 0,   1   }, % ,
-    [62257+gidoffset] = { 0,   1   }, % .
-    [62259+gidoffset] = { 0,   0.5 }, % :
-    [62260+gidoffset] = { 0,   0.5 }, % ;
-    [62270+gidoffset] = { 1,   0   }, % ‘
-    [62271+gidoffset] = { 0,   1   }, % ’
-    [62273+gidoffset] = { 0.5, 0   }, % “
-    [62274+gidoffset] = { 0,   0.5 }, % ”
+  fonts.protrusions.setups.notoserifcjk = {
+    [0x28]   = { 0.5, 0   }, % (
+    [0x29]   = { 0,   0.5 }, % )
+    [0x2C]   = { 0,   1   }, % ,
+    [0x2E]   = { 0,   1   }, % .
+    [0x3A]   = { 0,   0.5 }, % :
+    [0x3B]   = { 0,   0.5 }, % ;
+    [0xAB]   = { 0.5, 0   }, % «
+    [0xBB]   = { 0,   0.5 }, % »
+    [0x2018] = { 1,   0   }, % ‘
+    [0x2019] = { 0,   1   }, % ’
+    [0x201C] = { 0.5, 0   }, % “
+    [0x201D] = { 0,   0.5 }, % ”
   }
-  fonts.protrusions.setups.notoserifcjk = notoserifcjkkr_hb
+  fonts.protrusions.setups.notosanscjk = {
+    % compresspunctuations 가 이미 반각을 강제했으므로 여기선 0.5만 준다.
+    % +halt, +vhal 따위의 옵션이 주어진 때에도 마찬가지
+    [0x3001]  = { 0, .5 },
+    [0x3002]  = { 0, .5 },
+    [0xFF0C]  = { 0, .5 },
+    [0xFF0E]  = { 0, .5 },
+    [0xFE10]  = { 0, .5 },
+    [0xFE11]  = { 0, .5 },
+    [0xFE12]  = { 0, .5 },
+  }
 }
 \defaultfontfeatures+{Renderer=HarfBuzz}
 \setmainfont{Latin Modern Roman}[Expansion] % for hb tlig
@@ -87,6 +96,7 @@
   InterCharStretch=1pt,
   CharRaise=1pt,
   CharacterWidth=Full,
+  Protrusion=notosanscjk,
 ]
 \newhangulfontface\rubyfont{Noto Serif CJK KR Medium}[
   Script=Hangul,
@@ -124,7 +134,7 @@
 \author{Dohyun Kim \normalsize |<nomos at ktug org>| \and
         Soojin Nam \normalsize |<jsunam at gmail com>| \and
   \normalsize <\url{http://github.com/dohyunkim/luatexko}>}
-\date{Version 3.0\quad 2021/03/01}
+\date{Version 3.1\quad 2021/04/20}
 \maketitle
 
 \begin{quote}
@@ -455,6 +465,7 @@
 참조.
 
 \begin{figure}
+  \fboxsep=5pt
   \framebox[\linewidth]{
     \begin{vertical}{20em}
       \linespread{1.5}\sffamily \verticalhangulfont
@@ -465,17 +476,21 @@
 \end{figure}
 
 \begin{figure}
+  \fboxsep=5pt
   \framebox[\linewidth]{
-    \begin{vertical}{19em}
+    \begin{vertical}{17em}
       \linespread{1.5}\sffamily \verticalhangulfont
       \parindent0pt \everypar{\hangindent1em \hangafter1 }
       \analectstext
     \end{vertical}
   }
-  \caption{세로쓰기의 다른 예. 박스 높이 |19em|을 지시했다.
+  \caption{세로쓰기의 다른 예. 박스 높이 |17em|을 지시했다.
     글꼴에 |CompressPunctuations|와 더불어
     |InterCharStretch=1pt|를 옵션으로 주었다. 공백이 없는 문서는 자간늘이기 값을
     넉넉하게 주는 것이 좋다.
+    |Protrusion| 옵션으로 구두점들이 행 끝에 매달린 것도 볼 수 있다.
+    |CompressPunctuations|로 이미 반각이 강제되고 있으므로 글자내밀기 값으로 |0.5|를
+    주어야 전부 내밀기가 된다.
   }\label{fig:vertical3}
 \end{figure}
 
@@ -498,6 +513,7 @@
 가로쓰기 영역의 폰트 설정은 사용자의 몫이다.
 
 \begin{figure}
+  \fboxsep=5pt
   \framebox[\linewidth]{
     \begin{vertical}{17em}
       \linespread{1.5}\sffamily \verticalhangulfont

Modified: trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko-normalize.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko-normalize.lua	2021-04-20 20:13:46 UTC (rev 58927)
+++ trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko-normalize.lua	2021-04-20 20:14:00 UTC (rev 58928)
@@ -13,8 +13,8 @@
 
 luatexbase.provides_module({
   name        = "luatexko-normalize",
-  version     = "3.0",
-  date        = "2021/03/01",
+  version     = "3.1",
+  date        = "2021/04/20",
   author      = "Dohyun Kim, Soojin Nam",
   description = "Hangul normalization",
   license     = "LPPL v1.3+",

Modified: trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko-uhc2utf8.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko-uhc2utf8.lua	2021-04-20 20:13:46 UTC (rev 58927)
+++ trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko-uhc2utf8.lua	2021-04-20 20:14:00 UTC (rev 58928)
@@ -13,8 +13,8 @@
 
 luatexbase.provides_module({
   name        = "luatexko-uhc2utf8",
-  version     = "3.0",
-  date        = "2021/03/01",
+  version     = "3.1",
+  date        = "2021/04/20",
   author      = "Dohyun Kim, Soojin Nam",
   description = "UHC (CP949) input encoding",
   license     = "LPPL v1.3+",

Modified: trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua	2021-04-20 20:13:46 UTC (rev 58927)
+++ trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua	2021-04-20 20:14:00 UTC (rev 58928)
@@ -13,8 +13,8 @@
 
 luatexbase.provides_module {
   name        = 'luatexko',
-  date        = '2021/03/01',
-  version     = '3.0',
+  date        = '2021/04/20',
+  version     = '3.1',
   description = 'typesetting Korean with LuaTeX',
   author      = 'Dohyun Kim, Soojin Nam',
   license     = 'LPPL v1.3+',
@@ -112,9 +112,6 @@
 local hangulbyhangulattr = attributes.luatexkohangulbyhangulattr
 local hanjabyhanjaattr   = attributes.luatexkohanjabyhanjaattr
 
-local vert_classic = 1
-local SC_classic   = 2
-
 local stretch_f = 5/100 -- should be consistent for ruby
 
 local function get_font_data (fontid)
@@ -170,7 +167,7 @@
   end
 end
 
-local function is_harf (f)
+local function has_harf_data (f)
   if type(f) == "number" then
     f = get_font_data(f)
   end
@@ -177,24 +174,72 @@
   return f.hb
 end
 
-local function is_not_harf (f)
-  if option_in_font(f, "mode") == "harf" then -- mode=harf with non-luahbtex
-    return false
-  end
-  return not is_harf(f)
-end
+local harfbuzz = luaotfload.harfbuzz
+local os2tag = harfbuzz and harfbuzz.Tag.new"OS/2"
 
-local function harf_reordered_tonemark (curr)
-  if is_harf(curr.font) then
-    local props = getproperty(curr) or {}
-    local actualtext = props.luaotfload_startactualtext or ""
-    return actualtext:find"302[EF]$"
-  end
-end
+local fontoptions = {
+  is_not_harf = setmetatable( {}, { __index = function (t, fid)
+    if fid then
+      local bool = has_harf_data(fid) and false or true
+      t[fid] = bool
+      return bool
+    end
+  end }),
 
-local os2tag = luaotfload.harfbuzz and luaotfload.harfbuzz.Tag.new"OS/2"
+  mode = setmetatable( {}, { __index = function (t, fid)
+    if fid then
+      local m = option_in_font(fid, "mode") or false
+      if m == "harf" and not has_harf_data(fid) then
+        m = "node" -- default mode when 'mode=harf' in non-luahbtex
+      end
+      t[fid] = m
+      return m
+    end
+  end }),
 
-local font_options = {
+  is_widefont = setmetatable( {}, { __index = function(t, fid)
+    if fid then
+      local fontdata = get_font_data(fid)
+      local format   = fontdata.format
+      local encode   = fontdata.encodingbytes
+      local bool     = encode == 2 or format == "opentype" or format == "truetype"
+      t[fid] = bool
+      return bool
+    end
+  end }),
+
+  is_hangulscript = setmetatable( {}, { __index = function(t, fid)
+    if fid then
+      local bool = option_in_font(fid, "script") == "hang"
+      t[fid] = bool
+      return bool
+    end
+  end }),
+
+  compresspunctuations = setmetatable( {}, { __index = function(t, fid)
+    if fid then
+      local bool = option_in_font(fid, "compresspunctuations") or false
+      t[fid] = bool
+      return bool
+    end
+  end }),
+
+  removeclassicspaces = setmetatable( {}, { __index = function(t, fid)
+    if fid then
+      local bool = option_in_font(fid, "removeclassicspaces") or false
+      t[fid] = bool
+      return bool
+    end
+  end }),
+
+  slantvalue = setmetatable( {}, { __index = function(t, fid)
+    if fid then
+      local val = option_in_font(fid, "slant") or false
+      t[fid] = val
+      return val
+    end
+  end }),
+
   charraise = setmetatable( {}, { __index = function(t, fid)
     if fid then
       local dim = font_opt_dim(fid, "charraise") or false
@@ -247,7 +292,7 @@
   hangulspaceskip = setmetatable( {}, { __index = function(t, fid)
     if fid then
       local newwd
-      if is_harf(fid) then
+      if has_harf_data(fid) then
         newwd = getparameters(fid) or false
         if newwd then
           newwd = { newwd.space, newwd.space_stretch, newwd.space_shrink }
@@ -274,29 +319,27 @@
         t[fid] = true; return true
       end
       -- but not in harf mode; so we simply test widths of some glyphs
-      if is_harf(fid) then
-        local chars = get_font_data(fid).characters or {}
-        local i, M = chars[0x69], chars[0x4D]
-        if i and M and i.width == M.width then
-          t[fid] = true; return true
-        end
+      local chars = get_font_data(fid).characters or {}
+      local i, M = chars[0x69], chars[0x4D]
+      if i and M and i.width == M.width then
+        t[fid] = true; return true
       end
       t[fid] = false; return false
     end
   end } ),
 
-  tonemarkwidth = setmetatable( {}, { __index = function(t, fid)
+  tonemark_xmax = setmetatable( {}, { __index = function(t, fid)
     if fid then
-      local hwidth
-      -- check horizontal width; vertical width is mostly non-zero
+      -- check horizontal metric
       local fontdata     = get_font_data(fid)
       local shared       = fontdata.shared      or {}
       local rawdata      = shared.rawdata       or {}
       local descriptions = rawdata.descriptions or {}
       local description  = descriptions[0x302E] or {}
-      hwidth = description.width or 0
-      t[fid] = hwidth
-      return hwidth
+      local bbox         = description.boundingbox or {}
+      local xmax         = bbox[3] or -1
+      t[fid] = xmax
+      return xmax
     end
     return 0
   end } ),
@@ -307,7 +350,7 @@
       -- luaharfbuzz's Font:get_h_extents() gets ascender value from hhea table;
       -- Node mode's parameters.ascender is gotten from OS/2 table.
       -- TypoAscender in OS/2 table seems to be more suitable for our purpose.
-      local hb = is_harf(fid)
+      local hb = has_harf_data(fid)
       if hb and os2tag then
         local hbface = hb.shared.face
         local tags = hbface:get_table_tags()
@@ -348,6 +391,14 @@
   end
 end
 
+local function harf_reordered_tonemark (curr)
+  if not fontoptions.is_not_harf[curr.font] then
+    local props = getproperty(curr) or {}
+    local actualtext = props.luaotfload_startactualtext or ""
+    return actualtext:find"302[EF]$"
+  end
+end
+
 local function my_node_props (n)
   local t = getproperty(n)
   if not t then
@@ -358,8 +409,6 @@
   return t.luatexko
 end
 
-local active_processes = {}
-
 local function is_hanja (c)
   return c >= 0x3400 and c <= 0xA4C6
   or     c >= 0xF900 and c <= 0xFAD9
@@ -438,6 +487,135 @@
   or     is_jongsong(c)
 end
 
+local intercharclass = { [0] =
+  { [0] = nil,    {1,1},  nil,    {.5,.5} },
+  { [0] = nil,    nil,    nil,    {.5,.5} }, -- openers
+  { [0] = {1,1},  {1,1},  nil,    {.5,.5}, nil,    {1,1},  {1,1} }, -- closers
+  { [0] = {.5,.5},{.5,.5},{.5,.5},{1,.5},  {.5,.5},{.5,.5},{.5,.5},{.5,.5} }, -- middle dots
+  { [0] = {1,0},  {1,0},  nil,    {1.5,.5},nil,    {1,0},  {1,0} }, -- full stops
+  { [0] = nil,    {1,1},  nil,    {.5,.5} }, -- leaders and ellipses
+  { [0] = {1,1},  {1,1},  nil,    {.5,.5} }, -- questions and exclamations
+  { [0] = {.5,.5},{.5,.5},nil,    {.5,.5} }, -- vertical colons
+}
+
+local charclass = setmetatable({
+  [0x2018] = 1, [0x201C] = 1, [0x2329] = 1, [0x3008] = 1,
+  [0x300A] = 1, [0x300C] = 1, [0x300E] = 1, [0x3010] = 1,
+  [0x3014] = 1, [0x3016] = 1, [0x3018] = 1, [0x301A] = 1,
+  [0x301D] = 1, [0xFE17] = 1, [0xFE35] = 1, [0xFE37] = 1,
+  [0xFE39] = 1, [0xFE3B] = 1, [0xFE3D] = 1, [0xFE3F] = 1,
+  [0xFE41] = 1, [0xFE43] = 1, [0xFE47] = 1, [0xFF08] = 1,
+  [0xFF3B] = 1, [0xFF5B] = 1, [0xFF5F] = 1, [0xFF62] = 1,
+  --
+  [0x2019] = 2, [0x201D] = 2, [0x232A] = 2, [0x3001] = 2,
+  [0x3009] = 2, [0x300B] = 2, [0x300D] = 2, [0x300F] = 2,
+  [0x3011] = 2, [0x3015] = 2, [0x3017] = 2, [0x3019] = 2,
+  [0x301B] = 2, [0x301E] = 2, [0x301F] = 2, [0xFE10] = 2,
+  [0xFE11] = 2, [0xFE18] = 2, [0xFE36] = 2, [0xFE38] = 2,
+  [0xFE3A] = 2, [0xFE3C] = 2, [0xFE3E] = 2, [0xFE40] = 2,
+  [0xFE42] = 2, [0xFE44] = 2, [0xFE48] = 2, [0xFF09] = 2,
+  [0xFF0C] = 2, [0xFF3D] = 2, [0xFF5D] = 2, [0xFF60] = 2,
+  [0xFF63] = 2, [0xFF64] = 2,
+  --
+  [0x00B7] = 3, [0x30FB] = 3, [0xFF1A] = 3, [0xFF1B] = 3,
+  [0xFF65] = 3,
+  --
+  [0x3002] = 4, [0xFE12] = 4, [0xFF0E] = 4, [0xFF61] = 4,
+  --
+  [0x2015] = 5, [0x2025] = 5, [0x2026] = 5, [0xFE19] = 5,
+  [0xFE30] = 5, [0xFE31] = 5,
+  --
+  [0xFE15] = 6, [0xFE16] = 6, [0xFF01] = 6, [0xFF1F] = 6,
+}, { __index = function() return 0 end })
+
+local special_classes = {
+  [0] = charclass,
+  setmetatable({  -- vert
+    [0xFF1A] = 7, [0xFF1B] = 7,  -- 0xFE13, 0xFE14
+  }, { __index = charclass }),
+  setmetatable({  -- SC
+    [0xFF01] = 4, [0xFF1A] = 2, [0xFF1B] = 2, [0xFF1F] = 4,
+  }, { __index = charclass }),
+  setmetatable({  -- TC
+    [0x3001] = 3, [0x3002] = 3, [0xFF0C] = 3, [0xFF0E] = 3,
+  }, { __index = charclass }),
+  setmetatable({  -- TC vert
+    [0x3001] = 3, [0x3002] = 3, [0xFF0C] = 3, [0xFF0E] = 3,
+    [0xFF1A] = 7, [0xFF1B] = 7,  -- 0xFE13, 0xFE14
+  }, { __index = charclass }),
+}
+
+local function get_char_class (c, classic)
+  return special_classes[classic or 0][c]
+end
+
+local breakable_after = setmetatable({
+  [0x21] = true,   [0x22] = true,   [0x25] = true,   [0x27] = true,
+  [0x29] = true,   [0x2C] = true,   [0x2D] = true,   [0x2E] = true,
+  [0x3A] = true,   [0x3B] = true,   [0x3E] = true,   [0x3F] = true,
+  [0x5D] = true,   [0x7D] = true,   [0x7E] = true,   [0xBB] = true,
+  [0x2013] = true, [0x2014] = true, [0x25A1] = true, [0x25CB] = true,
+  [0x2E80] = true, [0x3003] = true, [0x3005] = true, [0x3007] = true,
+  [0x301C] = true, [0x3035] = true, [0x303B] = true, [0x303C] = true,
+  [0x309D] = true, [0x309E] = true, [0x30A0] = true, [0x30FC] = true,
+  [0x30FD] = true, [0x30FE] = true, [0xFE13] = true, [0xFE14] = true,
+  [0xFE32] = true, [0xFE50] = true, [0xFE51] = true, [0xFE52] = true,
+  [0xFE54] = true, [0xFE55] = true, [0xFE57] = true, [0xFE57] = true,
+  [0xFE58] = true, [0xFE5A] = true, [0xFE5C] = true, [0xFE5E] = true,
+  [0xFF1E] = true, [0xFF5E] = true, [0xFF70] = true, [0x226B] = true, -- ≫
+},{ __index = function (_,c)
+  return is_hangul_jamo(c) -- chosong also is breakable_after
+  or     is_noncjk_char(c)
+  or     is_hanja(c)
+  or     is_cjk_combining(c)
+  or     is_kana(c)
+  or     charclass[c] >= 2
+end })
+luatexko.breakableafter = breakable_after
+
+local breakable_before = setmetatable({
+  [0x28] = true,   [0x3C] = true,   [0x5B] = true,   [0x60] = true,
+  [0x7B] = true,   [0xAB] = true,   [0x25A1] = true, [0x25CB] = true,
+  [0x3007] = true, [0xFE59] = true, [0xFE5B] = true, [0xFE5D] = true,
+  [0xFF1C] = true, [0x226A] = true, -- ≪
+  -- small kana
+  [0x3041] = 1000, [0x3043] = 1000, [0x3045] = 1000, [0x3047] = 1000,
+  [0x3049] = 1000, [0x3063] = 1000, [0x3083] = 1000, [0x3085] = 1000,
+  [0x3087] = 1000, [0x308E] = 1000, [0x3095] = 1000, [0x3096] = 1000,
+  [0x30A1] = 1000, [0x30A3] = 1000, [0x30A5] = 1000, [0x30A7] = 1000,
+  [0x30A9] = 1000, [0x30C3] = 1000, [0x30E3] = 1000, [0x30E5] = 1000,
+  [0x30E7] = 1000, [0x30EE] = 1000, [0x30F5] = 1000, [0x30F6] = 1000,
+  [0x31F0] = 1000, [0x31F1] = 1000, [0x31F2] = 1000, [0x31F3] = 1000,
+  [0x31F4] = 1000, [0x31F5] = 1000, [0x31F6] = 1000, [0x31F7] = 1000,
+  [0x31F8] = 1000, [0x31F9] = 1000, [0x31FA] = 1000, [0x31FB] = 1000,
+  [0x31FC] = 1000, [0x31FD] = 1000, [0x31FE] = 1000, [0x31FF] = 1000,
+  [0xFF67] = 1000, [0xFF68] = 1000, [0xFF69] = 1000, [0xFF6A] = 1000,
+  [0xFF6B] = 1000, [0xFF6C] = 1000, [0xFF6D] = 1000, [0xFF6E] = 1000,
+  [0xFF6F] = 1000,  [0x1B150] = 1000, [0x1B151] = 1000, [0x1B152] = 1000,
+  [0x1B164] = 1000, [0x1B165] = 1000, [0x1B166] = 1000, [0x1B167] = 1000,
+},{ __index = function(_,c)
+  return is_hangul(c)
+  or     is_compat_jamo(c)
+  or     is_chosong(c)
+  or     is_hanja(c)
+  or     is_kana(c)
+  or     charclass[c] == 1
+end
+})
+luatexko.breakablebefore = breakable_before
+
+local function is_cjk_char (c)
+  return is_hangul_jamo(c)
+  or     is_hanja(c)
+  or     is_cjk_combining(c)
+  or     is_kana(c)
+  or     charclass[c] >= 1
+  or     rawget(breakable_before, c) and c >= 0x2000
+  or     rawget(breakable_after,  c) and c >= 0x2000
+end
+
+local active_processes = {}
+
 -- font fallback
 
 local force_hangul = {
@@ -479,7 +657,7 @@
     -- command does the same thing), which we will bypass here
     -- for alignment of CJK and Latin glyphs in verbatim environment.
     -- See http://www.ktug.org/xe/index.php?document_srl=249772
-    if not font_options.monospaced[curr.font] then
+    if not fontoptions.monospaced[curr.font] then
       local n = getnext(curr)
       if n and n.id == glueid and n.subtype == spaceskip then
         local params = getparameters(curr.font)
@@ -491,7 +669,7 @@
           and oldsto == 0
           and oldsho == 0 then -- not user's spaceskip
 
-          local newwd = font_options.hangulspaceskip[newfont]
+          local newwd = fontoptions.hangulspaceskip[newfont]
           if newwd then
             setglue(n, newwd[1], newwd[2], newwd[3])
           end
@@ -516,24 +694,28 @@
           if p.id == glyphid and curr.font ~= p.font then
             hangul_space_skip(curr, p.font)
             curr.font = p.font
+            curr.lang = p.lang
           end
 
           if not active_processes.reorderTM and
              hangul_tonemark[c] and
-             is_not_harf(curr.font) and
-             option_in_font(curr.font, "script") == "hang" then
+             fontoptions.is_not_harf[curr.font] and
+             fontoptions.is_hangulscript[curr.font] then
             luatexko.activate("reorderTM") -- activate reorderTM here
           end
 
         else
+          if curr.subtype == 1 and curr.lang ~= nohyphen and is_cjk_char(c) then
+            curr.lang = langkor -- suppress hyphenation of cjk chars
+          end
+
           local hf  = has_attribute(curr, hangulfontattr) or false
           local hjf = has_attribute(curr, hanjafontattr)  or false
-          local fontdata = get_font_data(curr.font)
-          local format   = fontdata.format
-          local encode   = fontdata.encodingbytes
-          local widefont = encode == 2 or format == "opentype" or format == "truetype"
 
-          if hf and widefont and force_hangul[c] and curr.lang ~= nohyphen and not font_options.monospaced[curr.font] then
+          if hf and force_hangul[c]
+            and fontoptions.is_widefont[curr.font] -- exclude legacy fonts
+            and curr.lang ~= nohyphen and not fontoptions.monospaced[curr.font] -- exclude ttfamily
+            then
             curr.font = hf
           elseif hf and has_attribute(curr, hangulbyhangulattr) and is_hangul_jamo(c) then
             hangul_space_skip(curr, hf)
@@ -541,7 +723,7 @@
           elseif hjf and has_attribute(curr, hanjabyhanjaattr) and is_hanja(c) then
             hangul_space_skip(curr, hjf)
             curr.font = hjf
-          elseif not char_in_font(fontdata, c) then
+          elseif not char_in_font(curr.font, c) then
             local fbf = has_attribute(curr, fallbackfontattr) or false
             for _,f in ipairs{ hf, hjf, fbf } do
               if f and char_in_font(f, c) then
@@ -577,120 +759,6 @@
 
 -- linebreak
 
-local intercharclass = { [0] =
-  { [0] = nil,    {1,1},  nil,    {.5,.5} },
-  { [0] = nil,    nil,    nil,    {.5,.5} },
-  { [0] = {1,1},  {1,1},  nil,    {.5,.5}, nil,    {1,1},  {1,1} },
-  { [0] = {.5,.5},{.5,.5},{.5,.5},{1,.5},  {.5,.5},{.5,.5},{.5,.5} },
-  { [0] = {1,0},  {1,0},  nil,    {1.5,.5},nil,    {1,0},  {1,0} },
-  { [0] = nil,    {1,1},  nil,    {.5,.5} },
-  { [0] = {1,1},  {1,1},  nil,    {.5,.5} },
-  { }, -- vertical colon
-}
-
-local charclass = setmetatable({
-  [0x2018] = 1, [0x201C] = 1, [0x2329] = 1, [0x3008] = 1,
-  [0x300A] = 1, [0x300C] = 1, [0x300E] = 1, [0x3010] = 1,
-  [0x3014] = 1, [0x3016] = 1, [0x3018] = 1, [0x301A] = 1,
-  [0x301D] = 1, [0xFE17] = 1, [0xFE35] = 1, [0xFE37] = 1,
-  [0xFE39] = 1, [0xFE3B] = 1, [0xFE3D] = 1, [0xFE3F] = 1,
-  [0xFE41] = 1, [0xFE43] = 1, [0xFE47] = 1, [0xFF08] = 1,
-  [0xFF3B] = 1, [0xFF5B] = 1, [0xFF5F] = 1, [0xFF62] = 1,
-  --
-  [0x2019] = 2, [0x201D] = 2, [0x232A] = 2, [0x3001] = 2,
-  [0x3009] = 2, [0x300B] = 2, [0x300D] = 2, [0x300F] = 2,
-  [0x3011] = 2, [0x3015] = 2, [0x3017] = 2, [0x3019] = 2,
-  [0x301B] = 2, [0x301E] = 2, [0x301F] = 2, [0xFE10] = 2,
-  [0xFE11] = 2, [0xFE18] = 2, [0xFE36] = 2, [0xFE38] = 2,
-  [0xFE3A] = 2, [0xFE3C] = 2, [0xFE3E] = 2, [0xFE40] = 2,
-  [0xFE42] = 2, [0xFE44] = 2, [0xFE48] = 2, [0xFF09] = 2,
-  [0xFF0C] = 2, [0xFF3D] = 2, [0xFF5D] = 2, [0xFF60] = 2,
-  [0xFF63] = 2, [0xFF64] = 2,
-  --
-  [0x00B7] = 3, [0x30FB] = 3, [0xFF1A] = 3, [0xFF1B] = 3,
-  [0xFF65] = 3,
-  --
-  [0x3002] = 4, [0xFE12] = 4, [0xFF0E] = 4, [0xFF61] = 4,
-  --
-  [0x2015] = 5, [0x2025] = 5, [0x2026] = 5, [0xFE19] = 5,
-  [0xFE30] = 5, [0xFE31] = 5,
-  --
-  [0xFE15] = 6, [0xFE16] = 6, [0xFF01] = 6, [0xFF1F] = 6,
-}, { __index = function() return 0 end })
-
-local SC_charclass = setmetatable({
-  [0xFF01] = 4, [0xFF1A] = 4, [0xFF1B] = 4, [0xFF1F] = 4,
-}, { __index = charclass })
-
-local vert_charclass = setmetatable({
-  [0xFF1A] = 7, -- 0xFE13
-  [0xFF1B] = 7, -- 0xFE14
-}, { __index = charclass })
-
-local function get_char_class (c, classic)
-  if classic == vert_classic then
-    return vert_charclass[c]
-  elseif classic == SC_classic then
-    return SC_charclass[c]
-  end
-  return charclass[c]
-end
-
-local breakable_after = setmetatable({
-  [0x21] = true,   [0x22] = true,   [0x25] = true,   [0x27] = true,
-  [0x29] = true,   [0x2C] = true,   [0x2D] = true,   [0x2E] = true,
-  [0x3A] = true,   [0x3B] = true,   [0x3E] = true,   [0x3F] = true,
-  [0x5D] = true,   [0x7D] = true,   [0x7E] = true,   [0xBB] = true,
-  [0x2013] = true, [0x2014] = true, [0x25A1] = true, [0x25CB] = true,
-  [0x2E80] = true, [0x3003] = true, [0x3005] = true, [0x3007] = true,
-  [0x301C] = true, [0x3035] = true, [0x303B] = true, [0x303C] = true,
-  [0x309D] = true, [0x309E] = true, [0x30A0] = true, [0x30FC] = true,
-  [0x30FD] = true, [0x30FE] = true, [0xFE13] = true, [0xFE14] = true,
-  [0xFE32] = true, [0xFE50] = true, [0xFE51] = true, [0xFE52] = true,
-  [0xFE54] = true, [0xFE55] = true, [0xFE57] = true, [0xFE57] = true,
-  [0xFE58] = true, [0xFE5A] = true, [0xFE5C] = true, [0xFE5E] = true,
-  [0xFF1E] = true, [0xFF5E] = true, [0xFF70] = true,
-},{ __index = function (_,c)
-  return is_hangul_jamo(c) -- chosong also is breakable_after
-  or     is_noncjk_char(c)
-  or     is_hanja(c)
-  or     is_cjk_combining(c)
-  or     is_kana(c)
-  or     charclass[c] >= 2
-end })
-luatexko.breakableafter = breakable_after
-
-local breakable_before = setmetatable({
-  [0x28] = true,   [0x3C] = true,   [0x5B] = true,   [0x60] = true,
-  [0x7B] = true,   [0xAB] = true,   [0x25A1] = true, [0x25CB] = true,
-  [0x3007] = true, [0xFE59] = true, [0xFE5B] = true, [0xFE5D] = true,
-  [0xFF1C] = true,
-  -- small kana
-  [0x3041] = 1000, [0x3043] = 1000, [0x3045] = 1000, [0x3047] = 1000,
-  [0x3049] = 1000, [0x3063] = 1000, [0x3083] = 1000, [0x3085] = 1000,
-  [0x3087] = 1000, [0x308E] = 1000, [0x3095] = 1000, [0x3096] = 1000,
-  [0x30A1] = 1000, [0x30A3] = 1000, [0x30A5] = 1000, [0x30A7] = 1000,
-  [0x30A9] = 1000, [0x30C3] = 1000, [0x30E3] = 1000, [0x30E5] = 1000,
-  [0x30E7] = 1000, [0x30EE] = 1000, [0x30F5] = 1000, [0x30F6] = 1000,
-  [0x31F0] = 1000, [0x31F1] = 1000, [0x31F2] = 1000, [0x31F3] = 1000,
-  [0x31F4] = 1000, [0x31F5] = 1000, [0x31F6] = 1000, [0x31F7] = 1000,
-  [0x31F8] = 1000, [0x31F9] = 1000, [0x31FA] = 1000, [0x31FB] = 1000,
-  [0x31FC] = 1000, [0x31FD] = 1000, [0x31FE] = 1000, [0x31FF] = 1000,
-  [0xFF67] = 1000, [0xFF68] = 1000, [0xFF69] = 1000, [0xFF6A] = 1000,
-  [0xFF6B] = 1000, [0xFF6C] = 1000, [0xFF6D] = 1000, [0xFF6E] = 1000,
-  [0xFF6F] = 1000,  [0x1B150] = 1000, [0x1B151] = 1000, [0x1B152] = 1000,
-  [0x1B164] = 1000, [0x1B165] = 1000, [0x1B166] = 1000, [0x1B167] = 1000,
-},{ __index = function(_,c)
-  return is_hangul(c)
-  or     is_compat_jamo(c)
-  or     is_chosong(c)
-  or     is_hanja(c)
-  or     is_kana(c)
-  or     charclass[c] == 1
-end
-})
-luatexko.breakablebefore = breakable_before
-
 local allowbreak_false_nodes = {
   [hlistid]   = true,
   [vlistid]   = true,
@@ -758,12 +826,12 @@
 
   dim = dim or 0
   local gl = nodenew(glueid)
-  local en = font_options.en_size[fid]
+  local en = fontoptions.en_size[fid]
   if ict then
     en = classic and en or en/4
     setglue(gl, en * ict[1] + dim, nil, en * ict[2])
   else
-    local str = font_options.intercharstretch[fid] or stretch_f*en
+    local str = fontoptions.intercharstretch[fid] or stretch_f*en
     setglue(gl, dim, str, str*0.6)
   end
 
@@ -777,7 +845,7 @@
     local ict = intercharclass[pcl][ccl]
     local brb = breakable_before[cc]
     local br  = brb and breakable_after[pc]
-    local dim = font_options.intercharacter[fid]
+    local dim = fontoptions.intercharacter[fid]
     if ict or br or dim and (pcl >= 1 or ccl >= 1) then
       head = insert_glue_before(head, curr, par, br, brb, old, ict, dim, fid)
     end
@@ -825,69 +893,13 @@
   return head
 end
 
--- compress punctuations
-
-local function process_glyph_width (head)
-  local curr = head
-  while curr do
-    local id = curr.id
-    if id == glyphid then
-      if curr.lang ~= nohyphen
-        and option_in_font(curr.font, "compresspunctuations") then
-
-        local cc = my_node_props(curr).unicode or curr.char
-        local old = has_attribute(curr, classicattr)
-        local class = get_char_class(cc, old)
-        if class >= 1 and class <= 4 and
-          (old or cc < 0x2000 or cc > 0x202F) then -- exclude general puncts
-
-          -- harf-node always puts kern after the glyph
-          local gpos = class == 1 and is_not_harf(curr.font) and getprev(curr) or getnext(curr)
-          gpos = gpos and gpos.id == kernid and gpos.subtype == fontkern
-
-          if not gpos then
-            local wd = font_options.en_size[curr.font] - curr.width
-            if wd ~= 0 then
-              local k = nodenew(kernid) -- fontkern (subtype 0) is default
-              k.kern = class == 3 and wd/2 or wd
-              if class == 1 then
-                head = insert_before(head, curr, k)
-              elseif class == 2 or class == 4 then
-                head, curr = insert_after(head, curr, k)
-              else
-                local k2 = nodecopy(k)
-                head = insert_before(head, curr, k)
-                head, curr = insert_after(head, curr, k2)
-              end
-            end
-          end
-        end
-      end
-    elseif id == mathid then
-      curr = end_of_math(curr)
-    end
-    curr = getnext(curr)
-  end
-  return head
-end
-
 -- interhangul & interlatincjk
 
-local function is_cjk_char (c)
-  return is_hangul_jamo(c)
-  or     is_hanja(c)
-  or     is_cjk_combining(c)
-  or     is_kana(c)
-  or     charclass[c] >= 1
-  or     rawget(breakable_before, c) and c >= 0x2000
-  or     rawget(breakable_after,  c) and c >= 0x2000
-end
-
 local function do_interhangul_option (head, curr, pc, c, fontid, par)
   local cc = (is_hangul(c) or is_compat_jamo(c) or is_chosong(c)) and 1 or 0
 
   if cc*pc == 1 and curr.lang ~= nohyphen then
-    local dim = font_options.interhangul[fontid]
+    local dim = fontoptions.interhangul[fontid]
     if dim then
       head = insert_glue_before(head, curr, par, true, true, false, false, dim, fontid)
     end
@@ -944,11 +956,11 @@
     local brb = cc == 2 or breakable_before[c] -- numletter != br_before
     if brb then
       local f = cc == 1 and cf or pf
-      local dim = font_options.interlatincjk[f]
+      local dim = fontoptions.interlatincjk[f]
       if dim then
         local ict = old and intercharclass[pcl][ccl] -- under classic env. only
         if ict then
-          dim = font_options.intercharacter[f] or 0
+          dim = fontoptions.intercharacter[f] or 0
         end
         head = insert_glue_before(head, curr, par, true, brb, old, ict, dim, f)
       end
@@ -1011,10 +1023,56 @@
   return head
 end
 
+-- compress punctuations
+
+local function process_glyph_width (head)
+  local curr = head
+  while curr do
+    local id = curr.id
+    if id == glyphid then
+      if curr.lang ~= nohyphen
+        and fontoptions.compresspunctuations[curr.font] then
+
+        local cc = my_node_props(curr).unicode or curr.char
+        local old = has_attribute(curr, classicattr)
+        local class = get_char_class(cc, old)
+        if class >= 1 and class <= 4 and
+          (old or cc < 0x2000 or cc > 0x202F) then -- exclude general puncts
+
+          -- harf-node always puts kern after the glyph
+          local gpos = class == 1 and fontoptions.is_not_harf[curr.font] and getprev(curr) or getnext(curr)
+          gpos = gpos and gpos.id == kernid and gpos.subtype == fontkern
+
+          if not gpos then
+            local wd = fontoptions.en_size[curr.font] - curr.width
+            if wd ~= 0 then
+              local k = nodenew(kernid) -- fontkern (subtype 0) is default
+              k.kern = class == 3 and wd/2 or wd
+              if class == 1 then
+                head = insert_before(head, curr, k)
+              elseif class == 2 or class == 4 then
+                head, curr = insert_after(head, curr, k)
+              else
+                local k2 = nodecopy(k)
+                head = insert_before(head, curr, k)
+                head, curr = insert_after(head, curr, k2)
+              end
+            end
+          end
+        end
+      end
+    elseif id == mathid then
+      curr = end_of_math(curr)
+    end
+    curr = getnext(curr)
+  end
+  return head
+end
+
 -- remove classic spaces
 
 local function process_remove_spaces (head)
-  local curr, opt_name, to_free = head, "removeclassicspaces", {}
+  local curr, to_free = head, {}
   while curr do
     local id = curr.id
     if id == glueid then
@@ -1037,7 +1095,7 @@
               elseif id == hlistid and v.list then
                 vchar, vfont = hbox_char_font(v, k == "n")
               end
-              if vchar and vfont and option_in_font(vfont, opt_name) then
+              if vchar and vfont and fontoptions.removeclassicspaces[vfont] then
                 ok = is_cjk_char(vchar)
               end
 
@@ -1243,7 +1301,7 @@
           end
 
           local currwd = curr.width
-          if currwd >= font_options.en_size[curr.font] then
+          if currwd >= fontoptions.en_size[curr.font] then
             local box = nodecopy(dotemphbox[dotattr]).list
             -- bypass unwanted nodes injected by some other packages
             while box.id ~= hlistid do
@@ -1289,7 +1347,7 @@
     local down
     local ex = get_font_param(f, "x_height") or texsp"1ex"
     if is_cjk_char(c) then
-      local ascender, descender = tableunpack(font_options.asc_desc[f])
+      local ascender, descender = tableunpack(fontoptions.asc_desc[f])
       if ascender and descender then
         down = descender - (ascender + descender)/2
       else
@@ -1299,7 +1357,7 @@
       down = -0.5*ex
     end
 
-    local raise = font_options.charraise[f] or 0
+    local raise = fontoptions.charraise[f] or 0
     return down - raise
   end
   return -texsp"0.5ex"
@@ -1423,9 +1481,9 @@
 
 function luatexko.getrubystretchfactor (box)
   local _, fid = hbox_char_font(box, true, true)
-  local str = font_options.intercharstretch[fid]
+  local str = fontoptions.intercharstretch[fid]
   if str then
-    local em = font_options.en_size[fid] * 2
+    local em = fontoptions.en_size[fid] * 2
     set_macro("luatexkostretchfactor", stringformat("%.4f", str/em/2))
   end
 end
@@ -1483,7 +1541,7 @@
           local shift = shift_put_top(curr, ruby)
 
           local _, f = hbox_char_font(curr, true, true)
-          local ascender = tableunpack(font_options.asc_desc[f]) or curr.height
+          local ascender = tableunpack(fontoptions.asc_desc[f]) or curr.height
           ruby.shift = shift - ascender - ruby.depth - ruby_t[2] -- rubysep
           head = insert_before(head, curr, ruby)
         end
@@ -1539,10 +1597,9 @@
   while curr do
     local id = curr.id
     if id == glyphid and
-       is_not_harf(curr.font) and
-       option_in_font(curr.font, "script") == "hang" then
+       fontoptions.is_not_harf[curr.font] and
+       fontoptions.is_hangulscript[curr.font] then
 
-      local fontdata = get_font_data(curr.font)
       local uni = my_node_props(curr).unicode or curr.char
       if is_hangul(uni) or is_chosong(uni) or uni == 0x25CC then
         init = curr
@@ -1559,7 +1616,7 @@
             n = getnext(n)
           end
 
-          if #syllable > 1 and font_options.tonemarkwidth[curr.font] ~= 0 then
+          if #syllable > 1 and fontoptions.tonemark_xmax[curr.font] >= 0 then
             local TM = curr
 
             local actual    = pdfliteral_direct_actual(syllable)
@@ -1572,10 +1629,10 @@
           end
 
           init = nil
-        elseif char_in_font(fontdata, 0x25CC) then -- isolated tone mark
+        elseif char_in_font(curr.font, 0x25CC) then -- isolated tone mark
           local dotcircle = nodecopy(curr)
           dotcircle.char = 0x25CC
-          if font_options.tonemarkwidth[curr.font] ~= 0 then
+          if fontoptions.tonemark_xmax[curr.font] >= 0 then
             local actual    = pdfliteral_direct_actual{ init = curr, uni }
             local endactual = pdfliteral_direct_actual()
             head = insert_before(head, curr, actual)
@@ -1845,7 +1902,7 @@
     if v and v > 0 then
       local amount = get_font_data(v).vertcharraise
       if amount then
-        amount = amount + (font_options.charraise[v] or 0)
+        amount = amount + (fontoptions.charraise[v] or 0)
         set_macro("luatexkohorizboxmoveright", texsp(amount).."sp")
         break
       end
@@ -1863,8 +1920,7 @@
     local id = curr.id
     if id == glyphid then
       if not has_attribute(curr,raiseattr) then
-        local f = curr.font
-        local raise = font_options.charraise[f]
+        local raise = fontoptions.charraise[curr.font]
         if raise then
           curr.yoffset = (curr.yoffset or 0) + raise
         end
@@ -1896,7 +1952,7 @@
               break
             end
 
-            local slant = option_in_font(p.font, "slant")
+            local slant = fontoptions.slantvalue[p.font]
             if slant and slant > 0 then
               tableinsert(t, char_in_font(p.font, p.char).italic or 0)
             end
@@ -1934,7 +1990,7 @@
     local params = fontdata.parameters or {}
     params.slant = (params.slant or 0) + fsl*65536 -- slant per point
 
-    local hb = is_harf(fontdata)
+    local hb = has_harf_data(fontdata)
     local scale  = hb and hb.scale or params.factor or 655.36
     local shared = fontdata.shared or {}
     local descrs = shared.rawdata and shared.rawdata.descriptions or {}
@@ -1968,6 +2024,13 @@
 
 -- wrap up
 
+add_to_callback ("hyphenate",
+function(head)
+  local head = process_fonts(head)
+  lang.hyphenate(head)
+end,
+"luatexko.hyphenate.fonts_and_languages")
+
 local pass_fun = function(...) return ... end
 create_callback("luatexko_prelinebreak_first",  "data", pass_fun)
 create_callback("luatexko_prelinebreak_second", "data", pass_fun)
@@ -1985,7 +2048,6 @@
            or gc == "vtop"
            or gc == "insert"
            or gc == "vcenter"
-  h = process_fonts(h)
   h = call_callback("luatexko_prelinebreak_first", h, par)
   h = call_callback("luatexko_prelinebreak_second", h, par)
   return process_linebreak(h, par)
@@ -2047,10 +2109,10 @@
   description = "raise chars",
   default = false,
   manipulators = {
-    node = function(fontdata)
+    node = function()
       activate_process("post_shaping_filter", process_charraise, "charraise")
     end,
-    plug = function(fontdata)
+    plug = function()
       activate_process("post_shaping_filter", process_charraise, "charraise")
     end,
   },
@@ -2123,26 +2185,24 @@
   },
 }
 
-local function process_protrusion (fontdata, _, value)
-  local setup = fonts.protrusions.setups[value] or {}
-  local quad  = fontdata.parameters.quad
-  for i, v in pairs(setup) do
-    local chr = fontdata.characters[i]
-    if chr then
-      local wdq = chr.width/quad
-      chr.left_protruding  = chr.left_protruding  or wdq*v[1]*1000
-      chr.right_protruding = chr.right_protruding or wdq*v[2]*1000
-    end
+local dir_ltr = harfbuzz and harfbuzz.Direction.new"ltr"
+
+local function get_HB_variant_char (fontdata, charcode)
+  local hbfont = fontdata.hb.shared.font
+  local spec   = fontdata.specification
+  local shaper = spec.features.raw.shaper
+  local buff   = harfbuzz.Buffer.new()
+  buff:set_direction(dir_ltr)
+  buff:set_script(spec.script)
+  buff:set_language(spec.language)
+  buff:add_codepoints{charcode}
+  harfbuzz.shape_full(hbfont, buff, spec.hb_features, shaper and {shaper} or {})
+  local glyphs = buff:get_glyphs()
+  if glyphs and glyphs[1] then
+    local glyph  = glyphs[1].codepoint
+    local offset = fontdata.hb.shared.gid_offset
+    return glyph + offset
   end
-  if tex.protrudechars == 0 then
-    texset("global", "protrudechars", 2)
-  end
-  if option_in_font(fontdata, "compresspunctuations") then
-    local fullname = fontdata.fullname
-    fontdata_warning("protrude."..fullname,
-    "Both `compresspunctuations' and `protrusion' are\nenabled for `%s'.\n"..
-    "Beware bad justifications.", fullname)
-  end
 end
 
 otfregister {
@@ -2150,8 +2210,41 @@
   description = "glyph protrusion",
   default = false,
   manipulators = {
-    node = process_protrusion,
-    plug = process_protrusion,
+    node = function(fontdata, _, value)
+      local setup = fonts.protrusions.setups[value] or {}
+      local quad  = fontdata.parameters.quad
+      for i, v in pairs(fontdata.characters) do
+        local uni = v.unicode
+        if uni then
+          local lr = setup[uni]
+          if lr then
+            local wdq = v.width/quad*1000
+            v.left_protruding  = wdq*lr[1]
+            v.right_protruding = wdq*lr[2]
+          end
+        end
+      end
+      if tex.protrudechars == 0 then
+        texset("global", "protrudechars", 2)
+      end
+    end,
+    plug = function(fontdata, _, value)
+      local setup = fonts.protrusions.setups[value] or {}
+      local quad  = fontdata.parameters.quad
+      for i, v in pairs(setup) do
+        for _, ii in ipairs{i, get_HB_variant_char(fontdata,i)} do
+          local chr = fontdata.characters[ii]
+          if chr then
+            local wdq = chr.width/quad*1000
+            chr.left_protruding  = wdq*v[1]
+            chr.right_protruding = wdq*v[2]
+          end
+        end
+      end
+      if tex.protrudechars == 0 then
+        texset("global", "protrudechars", 2)
+      end
+    end,
   },
 }
 
@@ -2181,32 +2274,6 @@
   active_processes[name] = true
 end
 
-add_to_callback ("hyphenate",
-function(head)
-  local curr = head
-  while curr do
-    local id = curr.id
-    if id == glyphid and curr.subtype == 1 and curr.lang ~= nohyphen then
-      local c = curr.char
-      if c then
-        if is_unicode_var_sel(c) then
-          local p = getprev(curr)
-          if p.id == glyphid then
-            curr.lang = p.lang
-          end
-        elseif is_cjk_char(c) then
-          curr.lang = langkor
-        end
-      end
-    elseif id == mathid then
-      curr = end_of_math(curr)
-    end
-    curr = getnext(curr)
-  end
-  lang.hyphenate(head)
-end,
-"luatexko.hyphenate.prevent_disc_nodes")
-
 -- aux functions
 
 function luatexko.deactivateall (str)

Modified: trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko.sty
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko.sty	2021-04-20 20:13:46 UTC (rev 58927)
+++ trunk/Master/texmf-dist/tex/luatex/luatexko/luatexko.sty	2021-04-20 20:14:00 UTC (rev 58928)
@@ -14,7 +14,7 @@
 \ifdefined\luatexkohangulfontattr \endinput\fi
 \ifdefined\selectfont
   \NeedsTeXFormat{LaTeX2e}[2020/10/01]
-  \ProvidesPackage{luatexko}[2021/03/01 v3.0 typesetting Korean with LuaTeX]
+  \ProvidesPackage{luatexko}[2021/04/20 v3.1 typesetting Korean with LuaTeX]
   \RequirePackage{luatexbase}
   \RequirePackage{fontspec}[2020/02/03]
 \else
@@ -55,12 +55,57 @@
   \DeclareHookRule{shipout/before}{.}{before}{luacolor}
 \fi
 % classic
-\protected\def\typesetclassic{\luatexkoclassicattr\z@\parindent1em }
-\protected\def\typesetvertical{\luatexkoclassicattr\@ne\parindent1em }
+\protected\def\typesetclassic{% 0
+  \ifnum\luatexkoclassicattr<\z@
+    \luatexkoclassicattr\z@
+    \parindent1em
+  \fi
+  }
+\protected\def\typesetvertical{% 1
+  \ifcase\luatexkoclassicattr
+    \luatexkoclassicattr\@ne
+    \parindent1em
+  \or
+  \or
+    \luatexkoclassicattr\@ne
+  \or
+    \luatexkoclassicattr=4\relax
+  \or
+  \else
+    \luatexkoclassicattr\@ne
+    \parindent1em
+  \fi
+  }
 \protected\def\typesetmodern{\unsetattribute\luatexkoclassicattr}
 \protected\def\inhibitglue{\hskip\z at skip}
-\protected\def\Schinese{\luatexkoclassicattr\tw@\parindent2em }
-\protected\def\Tchinese{\luatexkoclassicattr\thr@@\parindent2em } % +halt
+\protected\def\Schinese{% 2, 1
+  \ifcase\luatexkoclassicattr
+    \luatexkoclassicattr\tw@
+  \or
+  \or
+  \or
+    \luatexkoclassicattr\tw@
+  \or
+    \luatexkoclassicattr\@ne
+  \else
+    \luatexkoclassicattr\tw@
+  \fi
+  \parindent2em
+  }
+\protected\def\Tchinese{% 3, 4
+  \ifcase\luatexkoclassicattr
+    \luatexkoclassicattr\thr@@
+  \or
+    \luatexkoclassicattr=4\relax
+  \or
+    \luatexkoclassicattr\thr@@
+  \or
+  \or
+  \else
+    \luatexkoclassicattr\thr@@
+  \fi
+  \parindent2em
+  }
 \let\korean\typesetmodern \let\japanese\typesetclassic \let\chinese\Schinese
 % josa
 \count@"AC00



More information about the tex-live-commits mailing list.