summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/luatex/luatexko/luatexko.lua')
-rw-r--r--Master/texmf-dist/tex/luatex/luatexko/luatexko.lua1720
1 files changed, 1720 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua b/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua
new file mode 100644
index 00000000000..98000fc1a98
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua
@@ -0,0 +1,1720 @@
+-- $Id: luatexko.lua,v 1.29 2012/12/13 09:13:22 nomos Exp $
+--
+-- Copyright (c) 2013 Dohyun Kim <nomos at ktug org>
+--
+-- This work may be distributed and/or modified under the
+-- conditions of the LaTeX Project Public License, either version 1.3c
+-- of this license or (at your option) any later version.
+-- The latest version of this license is in
+-- http://www.latex-project.org/lppl.txt
+-- and version 1.3c or later is part of all distributions of LaTeX
+-- version 2006/05/20 or later.
+
+module('luatexko', package.seeall)
+
+local err,warn,info,log = luatexbase.provides_module({
+ name = 'luatexko',
+ date = '2013/05/10',
+ version = 1.0,
+ description = 'Korean linebreaking and font-switching',
+ author = 'Dohyun Kim',
+ license = 'LPPL v1.3+',
+})
+
+local stringbyte = string.byte
+local stringgsub = string.gsub
+local stringchar = string.char
+local stringfind = string.find
+local stringmatch = string.match
+local stringgmatch = string.gmatch
+local stringformat = string.format
+local mathfloor = math.floor
+local tex_round = tex.round
+local tex_sp = tex.sp
+local fontdefine = font.define
+
+local fontdata = fonts.hashes.identifiers
+local font_define_func = callback.find("define_font")
+local utf8char = unicode.utf8.char
+
+local remove_node = node.remove
+local insert_before = node.insert_before
+local insert_after = node.insert_after
+local copy_node = node.copy
+local traverse_id = node.traverse_id
+local traverse = node.traverse
+local has_attribute = node.has_attribute
+local unset_attribute = node.unset_attribute
+local set_attribute = node.set_attribute
+local nodecount = node.count
+local nodeslide = node.slide
+local nodedimensions = node.dimensions
+local nodetail = node.tail
+
+local finemathattr = luatexbase.attributes.finemathattr
+local cjtypesetattr = luatexbase.attributes.cjtypesetattr
+local dotemphattr = luatexbase.attributes.dotemphattr
+local autojosaattr = luatexbase.attributes.autojosaattr
+local luakorubyattr = luatexbase.attributes.luakorubyattr
+local hangfntattr = luatexbase.attributes.hangfntattr
+local hanjfntattr = luatexbase.attributes.hanjfntattr
+local luakoglueattr = luatexbase.new_attribute("luakoglueattr")
+local luakounicodeattr = luatexbase.new_attribute("luakounicodeattr")
+local quoteraiseattr = luatexbase.new_attribute("quoteraiseattr")
+
+local add_to_callback = luatexbase.add_to_callback
+
+local gluenode = node.id("glue")
+local gluespecnode = node.id("glue_spec")
+local glyphnode = node.id("glyph")
+local discnode = node.id("disc")
+local mathnode = node.id("math")
+local hlistnode = node.id("hlist")
+local vlistnode = node.id("vlist")
+local kernnode = node.id("kern")
+local penaltynode = node.id("penalty")
+local rulenode = node.id("rule")
+local whatsitnode = node.id("whatsit")
+local whatsitspecial = node.subtype("special")
+
+local new_glue = node.new(gluenode)
+local new_glue_spec = node.new(gluespecnode)
+local new_penalty = node.new(penaltynode)
+local new_kern = node.new(kernnode,1)
+local new_rule = node.new(rulenode)
+
+local emsize = 655360
+
+dotemphnode = {}
+rubynode = {}
+ulinebox = {}
+hangulfont = {}
+hangulcount = 0
+hanjafontforhanja = false
+
+local cjkclass = {
+ [0x2018] = 1, -- ‘
+ [0x201C] = 1, -- “
+ [0xFF08] = 1, -- (
+ [0xFE35] = 1, -- ( vert
+ [0x3014] = 1, -- 〔
+ [0xFE39] = 1, -- 〔 vert
+ [0xFF3B] = 1, -- [
+ [0xFE47] = 1, -- [ vert
+ [0xFF5B] = 1, -- {
+ [0xFE37] = 1, -- { vert
+ [0x3008] = 1, -- 〈
+ [0xFE3F] = 1, -- 〈 vert
+ [0x300A] = 1, -- 《
+ [0xFE3D] = 1, -- 《 vert
+ [0x300C] = 1, -- 「
+ [0xFE41] = 1, -- 「 vert
+ [0x300E] = 1, -- 『
+ [0xFE43] = 1, -- 『 vert
+ [0x3010] = 1, -- 【
+ [0xFE3B] = 1, -- 【 vert
+ [0x3001] = 2, -- 、
+ [0xFE11] = 2, -- 、 vert
+ [0xFF0C] = 2, -- ,
+ [0xFE10] = 2, -- , vert
+ [0x2019] = 2, -- ’
+ [0x201D] = 2, -- ”
+ [0xFF09] = 2, -- )
+ [0xFE36] = 2, -- ) vert
+ [0x3015] = 2, -- 〕
+ [0xFE3A] = 2, -- 〕 vert
+ [0xFF3D] = 2, -- ]
+ [0xFE48] = 2, -- ] vert
+ [0xFF5D] = 2, -- }
+ [0xFE38] = 2, -- } vert
+ [0x3009] = 2, -- 〉
+ [0xFE40] = 2, -- 〉 vert
+ [0x300B] = 2, -- 》
+ [0xFE3E] = 2, -- 》 vert
+ [0x300D] = 2, -- 」
+ [0xFE42] = 2, -- 」 vert
+ [0x300F] = 2, -- 』
+ [0xFE44] = 2, -- 』 vert
+ [0x3011] = 2, -- 】
+ [0xFE3C] = 2, -- 】 vert
+ [0x00B7] = 3, -- ·
+ [0x30FB] = 3, -- ・
+ [0xFF1A] = 3, -- :
+ [0xFE13] = 3, -- : vert
+ [0xFF1B] = 3, -- ;
+ [0xFE14] = 3, -- ; vert
+ [0x3002] = 4, -- 。
+ [0xFE12] = 4, -- 。 vert
+ [0xFF0E] = 4, -- .
+ [0x2015] = 5, -- ―
+ [0x2026] = 5, -- …
+ [0xFE19] = 5, -- … vert
+ [0x2025] = 5, -- ‥
+ [0xFE30] = 5, -- ‥ vert
+ [0xFF1F] = 6, -- ?
+ [0xFF01] = 6, -- !
+}
+
+local inhibitxspcode = {
+ [0x002D] = 0, -- - hyphen minus
+ [0x003C] = 0, -- <
+ [0x003E] = 1, -- >
+ [0x00B0] = 1,
+ [0x2015] = 0,
+ [0x2018] = 2,
+ [0x2019] = 1,
+ [0x201C] = 2,
+ [0x201D] = 1,
+ [0x2026] = 0,
+ [0xFE19] = 0,
+ [0x2032] = 1,
+ [0x2033] = 1,
+ [0x3001] = 1,
+ [0xFE11] = 1,
+ [0x3002] = 1,
+ [0xFE12] = 1,
+ [0x3008] = 2,
+ [0xFE3F] = 2,
+ [0x3009] = 1,
+ [0xFE40] = 1,
+ [0x300A] = 2,
+ [0xFE3D] = 2,
+ [0x300B] = 1,
+ [0xFE3E] = 1,
+ [0x300C] = 2,
+ [0xFE41] = 2,
+ [0x300D] = 1,
+ [0xFE42] = 1,
+ [0x300E] = 2,
+ [0xFE43] = 2,
+ [0x300F] = 1,
+ [0xFE44] = 1,
+ [0x3010] = 2,
+ [0xFE3B] = 2,
+ [0x3011] = 1,
+ [0xFE3C] = 1,
+ [0x3012] = 2, -- 〒
+ [0x3014] = 2,
+ [0xFE39] = 2,
+ [0x3015] = 1,
+ [0xFE3A] = 1,
+ [0x301C] = 0,
+ [0xFF08] = 2,
+ [0xFE35] = 2,
+ [0xFF09] = 1,
+ [0xFE36] = 1,
+ [0xFF0C] = 1,
+ [0xFE10] = 1,
+ [0xFF0E] = 1,
+ [0xFF1B] = 1,
+ [0xFE14] = 1,
+ [0xFF1F] = 1,
+ [0xFF3B] = 2,
+ [0xFE47] = 2,
+ [0xFF3D] = 1,
+ [0xFE48] = 1,
+ [0xFF5B] = 2,
+ [0xFE37] = 2,
+ [0xFF5D] = 1,
+ [0xFE38] = 1,
+ [0xFFE5] = 0,
+}
+
+local postbreakpenalty = {
+ [0x0023] = 500,
+ [0x0024] = 500,
+ [0x0025] = 500,
+ [0x0026] = 500,
+ [0x0028] = 10000,
+ [0x003C] = 10000, -- <
+ [0x005B] = 10000,
+ [0x0060] = 10000,
+ [0x2013] = 50, -- en-dash
+ [0x2014] = 50, -- em-dash
+ [0x2018] = 10000,
+ [0x201C] = 10000,
+ [0x3008] = 10000,
+ [0xFE3F] = 10000,
+ [0x300A] = 10000,
+ [0xFE3D] = 10000,
+ [0x300C] = 10000,
+ [0xFE41] = 10000,
+ [0x300E] = 10000,
+ [0xFE43] = 10000,
+ [0x3010] = 10000,
+ [0xFE3B] = 10000,
+ [0x3014] = 10000,
+ [0xFE39] = 10000,
+ [0xFF03] = 200,
+ [0xFF04] = 200,
+ [0xFF05] = 200,
+ [0xFF06] = 200,
+ [0xFF08] = 10000,
+ [0xFE35] = 10000,
+ [0xFF3B] = 10000,
+ [0xFE47] = 10000,
+ [0xFF40] = 10000,
+ [0xFF5B] = 10000,
+ [0xFE37] = 10000,
+}
+
+local prebreakpenalty = {
+ [0x0021] = 10000,
+ [0x0022] = 10000,
+ [0x0027] = 10000,
+ [0x0029] = 10000,
+ [0x002A] = 500,
+ [0x002B] = 500,
+ [0x002C] = 10000,
+ [0x002D] = 10000,
+ [0x002E] = 10000,
+ [0x002F] = 500,
+ [0x003A] = 10000,
+ [0x003B] = 10000,
+ [0x003E] = 10000, -- >
+ [0x003F] = 10000,
+ [0x005D] = 10000,
+ [0x00B4] = 10000,
+ [0x00B7] = 10000, -- ·
+ [0x2013] = 10000, -- –
+ [0x2014] = 10000, -- —
+ [0x2015] = 10000,
+ [0x2019] = 10000,
+ [0x201D] = 10000,
+ [0x2025] = 250,
+ [0xFE30] = 250,
+ [0x2026] = 250,
+ [0xFE19] = 250,
+ [0x2212] = 200,
+ [0x3001] = 10000,
+ [0xFE11] = 10000,
+ [0x3002] = 10000,
+ [0xFE12] = 10000,
+ [0x3005] = 10000,
+ [0x3009] = 10000,
+ [0xFE40] = 10000,
+ [0x300B] = 10000,
+ [0xFE3E] = 10000,
+ [0x300D] = 10000,
+ [0xFE42] = 10000,
+ [0x300F] = 10000,
+ [0xFE44] = 10000,
+ [0x3011] = 10000,
+ [0xFE3C] = 10000,
+ [0x3015] = 10000,
+ [0xFE3A] = 10000,
+ [0x3041] = 150,
+ [0x3043] = 150,
+ [0x3045] = 150,
+ [0x3047] = 150,
+ [0x3049] = 150,
+ [0x3063] = 150,
+ [0x3083] = 150,
+ [0x3085] = 150,
+ [0x3087] = 150,
+ [0x308E] = 150,
+ [0x309B] = 10000,
+ [0x309C] = 10000,
+ [0x30A1] = 150,
+ [0x30A3] = 150,
+ [0x30A5] = 150,
+ [0x30A7] = 150,
+ [0x30A9] = 150,
+ [0x30C3] = 150,
+ [0x30E3] = 150,
+ [0x30E5] = 150,
+ [0x30E7] = 150,
+ [0x30EE] = 150,
+ [0x30F5] = 150,
+ [0x30F6] = 150,
+ [0x30FB] = 10000,
+ [0x30FC] = 10000,
+ [0xFF01] = 10000,
+ [0xFF09] = 10000,
+ [0xFE36] = 10000,
+ [0xFF0B] = 200,
+ [0xFF0C] = 10000,
+ [0xFE10] = 10000,
+ [0xFF0E] = 10000,
+ [0xFF1A] = 10000,
+ [0xFE13] = 10000,
+ [0xFF1B] = 10000,
+ [0xFE14] = 10000,
+ [0xFF1D] = 200,
+ [0xFF1F] = 10000,
+ [0xFF3D] = 10000,
+ [0xFE48] = 10000,
+ [0xFF5D] = 10000,
+ [0xFE38] = 10000,
+}
+
+local xspcode = {
+ [0x0027] = 2,
+ [0x0028] = 1,
+ [0x0029] = 2,
+ [0x002C] = 2,
+ [0x002E] = 2,
+ [0x003B] = 2,
+ [0x005B] = 1,
+ [0x005D] = 2,
+ [0x0060] = 1,
+}
+
+local cjk_glue_spec = { [0] =
+-- 한자 ( ) · . — ? 한글 초성 중종성 latin
+{[0] = nil, {.5,.5}, nil, {.25,.25},nil, nil, nil, nil, nil, nil, nil, }, --한자
+{[0] = nil, nil, nil, {.25,.25},nil, nil, nil, nil, nil, nil, nil, }, -- (
+{[0] = {.5,.5}, {.5,.5}, nil, {.25,.25},nil, {.5,.5}, {.5,.5}, {.25,.25},{.25,.25},{.25,.25},{.5,.5}, }, -- )
+{[0] = {.25,.25},{.25,.25},{.25,.25},{.5,.25},{.25,.25},{.25,.25},{.25,.25},{.25,.25},{.25,.25},{.25,.25},{.25,.25},}, -- ·
+{[0] = {.5,0}, {.5,0}, nil, {.75,.25},nil, {.5,0}, {.5,0}, {.5,0}, {.5,0}, {.5,0}, {.5,0}, }, -- .
+{[0] = nil, {.5,.5}, nil, {.25,.25},nil, nil, nil, nil, nil, nil, nil, }, -- —
+{[0] = {.5,.5}, {.5,.5}, nil, {.25,.25},nil, nil, nil, {.5,.5}, {.5,.5}, {.5,.5}, {.5,.5}, }, -- ?
+--
+{[0] = nil, {.25,.25},nil, {.25,.25},nil, nil, nil, nil, nil, nil, nil, }, --한글
+{[0] = nil, {.25,.25},nil, {.25,.25},nil, nil, nil, nil, nil, nil, nil, }, --초성
+{[0] = nil, {.25,.25},nil, {.25,.25},nil, nil, nil, nil, nil, nil, nil, }, --중종성
+{[0] = nil, {.5,.5}, nil, {.25,.25},nil, nil, nil, nil, nil, nil, nil, }, --latin
+}
+
+local latin_fullstop = {
+ [0x2e] = 1,
+ [0x21] = 2,
+ [0x3f] = 2,
+ [0x2026] = 1, -- \ldots
+}
+
+local latin_quotes = {
+ [0x0028] = 0x0029, -- ( )
+ [0x2018] = 0x2019, -- ‘ ’
+ [0x201C] = 0x201D, -- “ ”
+}
+
+local josa_list = { -- automatic josa selection
+ -- 리을, 중성, 종성
+ [0xAC00] = {0xC774, 0xAC00, 0xC774}, -- 가 = 이, 가, 이
+ [0xC740] = {0xC740, 0xB294, 0xC740}, -- 은 = 은, 는, 은
+ [0xC744] = {0xC744, 0xB97C, 0xC744}, -- 을 = 을, 를, 을
+ [0xC640] = {0xACFC, 0xC640, 0xACFC}, -- 와 = 과, 와, 과
+ [0xC73C] = {-1, -1, 0xC73C}, -- 으(로) = , , 으
+ [0xC774] = {0xC774, -1, 0xC774}, -- 이(라) = 이, , 이
+}
+
+local josa_code = {
+ [0x30] = 3, -- 0
+ [0x31] = 1, -- 1
+ [0x33] = 3, -- 3
+ [0x36] = 3, -- 6
+ [0x37] = 1, -- 7
+ [0x38] = 1, -- 8
+ [0x4C] = 1, -- L
+ [0x4D] = 3, -- M
+ [0x4E] = 3, -- N
+ [0x6C] = 1, -- l
+ [0x6D] = 3, -- m
+ [0x6E] = 3, -- n
+ [0xFB02] = 1, -- fl
+ [0xFB04] = 1, -- ffl
+ ng = 3,
+ ap = 3,
+ up = 3,
+ at = 3,
+ et = 3,
+ it = 3,
+ ot = 3,
+ ut = 3,
+ ok = 3,
+ ic = 3,
+ le = 1,
+ ime = 3,
+ ine = 3,
+ ack = 3,
+ ick = 3,
+ oat = 2,
+ TEX = 3,
+ -- else 2
+}
+
+local function get_gluenode (w,st,sh)
+ local g = copy_node(new_glue)
+ local s = copy_node(new_glue_spec)
+ s.width, s.stretch, s.shrink = w or 0, st or 0, sh or 0
+ g.spec = s
+ return g
+end
+
+local function get_penaltynode (n)
+ local p = copy_node(new_penalty)
+ p.penalty = n or 0
+ return p
+end
+
+local function get_kernnode (n)
+ local k = copy_node(new_kern)
+ k.kern = n or 0
+ return k
+end
+
+local function get_rulenode (w,h,d)
+ local r = copy_node(new_rule)
+ r.width, r.height, r.depth = w or 0, h or 0, d or 0
+ return r
+end
+
+local function make_luako_glue(...)
+ local glue = get_gluenode(...)
+ set_attribute(glue,luakoglueattr,1)
+ return glue
+end
+
+local function is_cjk_k (c)
+ return (c >= 0x2E80 and c <= 0x9FFF )
+ or (c >= 0xF900 and c <= 0xFAFF )
+ or (c >= 0xFE10 and c <= 0xFE1F )
+ or (c >= 0xFE30 and c <= 0xFE4F )
+ or (c >= 0xFF00 and c <= 0xFFEF )
+ or (c >= 0x20000 and c <= 0x2A6DF)
+ or (c >= 0x2F800 and c <= 0x2FA1F)
+ or c == 0x00B0 or c == 0x2015
+ -- or c == 0x2018 or c == 0x2019
+ -- or c == 0x201C or c == 0x201D
+ or c == 0x2026 or c == 0x2032
+ or c == 0x2033
+end
+
+local function is_hanja (c)
+ return (c >= 0x3400 and c <= 0x9FFF )
+ or (c >= 0xF900 and c <= 0xFAFF )
+ or (c >= 0x20000 and c <= 0x2A6DF)
+ or (c >= 0x2F800 and c <= 0x2FA1F)
+end
+
+local function is_hangul (c)
+ return (c >= 0xAC00 and c <= 0xD7A3)
+end
+
+local function is_chosong (c)
+ return (c >= 0x1100 and c <= 0x115F)
+ or (c >= 0xA960 and c <= 0xA97C)
+end
+
+local function is_jungjongsong (c)
+ return (c >= 0x1160 and c <= 0x11FF)
+ or (c >= 0xD7B0 and c <= 0xD7Fb)
+ or c == 0x302E or c == 0x302F -- tone marks
+end
+
+local function get_cjk_class (ch, cjtype)
+ if ch then
+ if is_hangul(ch) then return 7 end -- hangul = 7
+ if is_chosong(ch) then return 8 end -- jamo LC = 8
+ if is_jungjongsong(ch) then return 9 end -- jamo VL, TC, TM = 9
+ local c = is_cjk_k(ch) and 0 or 10 -- hanja = 0; latin = 10
+ if cjkclass[ch] then c = cjkclass[ch] end -- cjkclass 1 .. 6
+ if cjtype then
+ if cjtype == 2 and
+ (ch == 0xFF1F or ch == 0xFF01 or ch == 0xFF1A or ch == 0xFF1B) then
+ c = 4 -- simplified chinese ? !
+ end
+ else
+ if ch == 0x2018 or ch == 0x2019 or ch == 0x201C or ch == 0x201D then
+ c = 10 -- korean “ ” ‘ ’
+ end
+ end
+ return c
+ end
+ return nil
+end
+
+local function get_font_table (fid)
+ if fid then
+ if fontdata[fid] then
+ return fontdata[fid]
+ else
+ return font.getfont(fid)
+ end
+ end
+end
+
+local function get_font_char (fid, chr)
+ local f = get_font_table(fid)
+ return chr and f and f.characters and f.characters[chr]
+end
+
+local function get_font_emsize(fid)
+ local f = get_font_table(fid)
+ return (f and f.parameters and f.parameters.quad) or emsize
+end
+
+local function get_font_feature (fid, name)
+ local f = get_font_table(fid)
+ return f and f.shared and f.shared.features and f.shared.features[name]
+end
+
+local function get_char_boundingbox(fid, chr)
+ local f = get_font_table(fid)
+ local glbox = f and f.shared
+ glbox = glbox and glbox.rawdata
+ glbox = glbox and glbox.descriptions
+ glbox = glbox and glbox[chr] and glbox[chr].boundingbox
+ if glbox then
+ local factor, bbox = f.parameters and f.parameters.factor or 655.36, {}
+ for i,v in ipairs(glbox) do
+ bbox[i] = v * factor
+ end
+ return bbox
+ end
+end
+
+local function get_unicode_char(curr)
+ if curr.char > 0 and curr.char < 0xF0000 then return curr.char end
+ local uni = has_attribute(curr, luakounicodeattr)
+ if uni then return uni end
+ return curr.char
+end
+
+----------------------------
+-- cjk linebreak and spacing
+----------------------------
+local function get_hlist_class_first (hlist)
+ local curr = hlist.head
+ while curr do
+ if curr.id == glyphnode then
+ local c,f = get_unicode_char(curr), curr.font
+ if c then return c,f end
+ elseif curr.id == hlistnode or curr.id == vlistnode then
+ local c,f = get_hlist_class_first(curr)
+ if c then return c,f end
+ elseif curr.id == gluenode then
+ if curr.spec and curr.spec.width ~= 0 then return end
+ end
+ curr = curr.next
+ end
+end
+
+local function get_hlist_class_last (hlist,prevchar,prevfont)
+ local curr = nodeslide(hlist.head)
+ while curr do
+ if curr.id == glyphnode then
+ local c,f = get_unicode_char(curr), curr.font
+ if c then return c,f end
+ elseif curr.id == hlistnode or curr.id == vlistnode then
+ local c,f = get_hlist_class_last(curr)
+ if c then return c,f end
+ elseif curr.id == gluenode then
+ if curr.spec and curr.spec.width ~= 0 then return end
+ end
+ curr = curr.prev
+ end
+ return prevchar, prevfont
+end
+
+local function kanjiskip (head,curr)
+ insert_before(head,curr,make_luako_glue(0, emsize*0.1, emsize*0.02))
+end
+
+local function xkanjiskip (head,curr)
+ if has_attribute(curr,finemathattr) == 0 then -- ttfamily
+ kanjiskip(head,curr)
+ else
+ insert_before(head,curr,make_luako_glue(0.25*emsize, emsize*0.15, emsize*0.06))
+ end
+end
+
+local function interhangulskip (head,curr,currfont,prevfont,was_penalty)
+ local width = 0
+ local interhangul = get_font_feature(currfont, "interhangul")
+ if interhangul and currfont == prevfont then
+ width = tex_sp(interhangul)
+ end
+ if not was_penalty then
+ insert_before(head,curr,get_penaltynode(50))
+ end
+ insert_before(head,curr,make_luako_glue(width, emsize*0.04, emsize*0.02))
+end
+
+local function interhanjaskip (head,curr,was_penalty)
+ if not was_penalty then
+ insert_before(head,curr,get_penaltynode(50))
+ end
+ insert_before(head,curr,make_luako_glue(0, emsize*0.04, emsize*0.02))
+end
+
+local function koreanlatinskip (head,curr,currfont,prevfont,was_penalty)
+ local width = 0 -- default: 0em
+ if has_attribute(curr,finemathattr) == 1 then -- not ttfamily
+ local latincjk = get_font_feature(currfont, "interlatincjk")
+ if not latincjk then
+ latincjk = get_font_feature(prevfont, "interlatincjk")
+ end
+ if latincjk then
+ width = tex_sp(latincjk)
+ end
+ end
+ if not was_penalty then
+ insert_before(head,curr,get_penaltynode(50))
+ end
+ insert_before(head,curr,make_luako_glue(width, emsize*0.04, emsize*0.02))
+end
+
+local function cjk_insert_nodes(head,curr,currchar,currfont,prevchar,prevfont)
+ local was_penalty = false
+ local currentcjtype = has_attribute(curr,cjtypesetattr)
+ local p = get_cjk_class(prevchar, currentcjtype)
+ local c = get_cjk_class(currchar, currentcjtype)
+ ---[[raise latin puncts
+ if curr.id == glyphnode and has_attribute(curr,finemathattr) == 1 then -- not ttfamily
+ if c < 10 then -- not ttfamily
+ local nn, raise = curr.next, nil
+ while nn do
+ if nn.id == glyphnode and currfont ~= nn.font and latin_fullstop[nn.char] then
+ if not raise then
+ raise = get_font_feature(currfont, "punctraise")
+ raise = raise and tex_sp(raise)
+ end
+ if raise then
+ nn.yoffset = nn.yoffset or 0
+ nn.yoffset = nn.yoffset + raise
+ end
+ nn = nn.next
+ elseif nn.id == kernnode then
+ nn = nn.next
+ else
+ break
+ end
+ end
+ elseif latin_quotes[currchar] and not has_attribute(curr,quoteraiseattr) then
+ local nn, raise, cjkfont, depth, todotbl = curr.next, nil, nil, 1, {curr}
+ while nn do
+ if nn.id == glyphnode then
+ if latin_quotes[nn.char] == latin_quotes[currchar] then
+ depth = depth + 1
+ todotbl[#todotbl + 1] = nn
+ elseif nn.char == latin_quotes[currchar] then
+ depth = depth - 1
+ todotbl[#todotbl + 1] = nn
+ if depth == 0 then
+ if raise and nn.font == currfont and cjkfont ~= currfont then
+ for _,n in ipairs(todotbl) do
+ n.yoffset = n.yoffset or 0
+ n.yoffset = n.yoffset + raise
+ end
+ end
+ for _,n in ipairs(todotbl) do
+ set_attribute(n, quoteraiseattr, 1)
+ end
+ break
+ end
+ elseif not raise then
+ if get_cjk_class(get_unicode_char(nn)) < 10 then
+ raise = get_font_feature(nn.font, "quoteraise")
+ raise = raise and tex_sp(raise)
+ if raise then cjkfont = nn.font end
+ end
+ end
+ end
+ nn = nn.next
+ end
+ end
+ end
+ --raise latin puncts]]
+ if prebreakpenalty[currchar] then
+ was_penalty = true
+ insert_before(head,curr,get_penaltynode(prebreakpenalty[currchar]))
+ elseif postbreakpenalty[prevchar] then
+ was_penalty = true
+ insert_before(head,curr,get_penaltynode(postbreakpenalty[prevchar]))
+ end
+ ---[[ kern is a breakpoint if followed by a glue: protrusion and compress_fullwidth_punctuations
+ if (c and c == 1) or (p and p >= 2 and p <= 6) then was_penalty = true end
+ --]]
+ if p and c then
+ if currentcjtype then
+ if cjk_glue_spec[p] and cjk_glue_spec[p][c] then
+ local width = emsize * cjk_glue_spec[p][c][1]
+ local stretch = 0
+ local shrink = emsize * cjk_glue_spec[p][c][2]
+ insert_before(head,curr,make_luako_glue(width, stretch, shrink))
+ elseif p < 10 and c < 10 and p ~= 8 and c ~= 9 then
+ kanjiskip(head,curr)
+ elseif (p < 10 and c == 10) or (p == 10 and c < 10) then
+ if xspcode[currchar] then
+ if xspcode[currchar] % 2 == 1 then
+ xkanjiskip(head,curr)
+ end
+ elseif xspcode[prevchar] then
+ if xspcode[prevchar] > 1 then
+ xkanjiskip(head,curr)
+ end
+ elseif inhibitxspcode[currchar] then -- 3, 2
+ if inhibitxspcode[currchar] > 1 then
+ xkanjiskip(head,curr)
+ end
+ elseif inhibitxspcode[prevchar] then -- 3, 1
+ if inhibitxspcode[prevchar] % 2 == 1 then
+ xkanjiskip(head,curr)
+ end
+ else
+ xkanjiskip(head,curr)
+ end
+ end
+ else
+ if (p < 10 and c == 10) or (p == 10 and c < 10) then
+ if xspcode[currchar] then
+ if xspcode[currchar] % 2 == 1 then
+ koreanlatinskip(head,curr,currfont,prevfont,was_penalty)
+ end
+ elseif xspcode[prevchar] then
+ if xspcode[prevchar] > 1 then
+ koreanlatinskip(head,curr,currfont,prevfont,was_penalty)
+ end
+ elseif inhibitxspcode[currchar] then -- 3, 2
+ if inhibitxspcode[currchar] > 1 then
+ koreanlatinskip(head,curr,currfont,prevfont,was_penalty)
+ end
+ elseif inhibitxspcode[prevchar] then -- 3, 1
+ if inhibitxspcode[prevchar] % 2 == 1 then
+ koreanlatinskip(head,curr,currfont,prevfont,was_penalty)
+ end
+ else
+ koreanlatinskip(head,curr,currfont,prevfont,was_penalty)
+ end
+ elseif cjk_glue_spec[p] and cjk_glue_spec[p][c] then
+ koreanlatinskip(head,curr,currfont,prevfont,was_penalty)
+ elseif p == 7 and c == 7 then
+ interhangulskip(head,curr,currfont,prevfont,was_penalty)
+ elseif p < 10 and c < 10 and p ~= 8 and c ~= 9 then
+ interhanjaskip(head,curr,was_penalty)
+ end
+ end
+ end
+ -- for dot emphasis
+ if has_attribute(curr,dotemphattr)
+ and c ~= 0
+ and c ~= 7
+ and p ~= 8
+ and c ~= 8 then
+ unset_attribute(curr,dotemphattr)
+ end
+
+ return currchar,currfont
+end
+
+local function cjk_spacing_linebreak (head)
+ local prevchar,prevfont = nil,nil
+ for curr in traverse(head) do
+ if has_attribute(curr,finemathattr) then
+ if curr.id == gluenode then
+ prevchar,prevfont = nil,nil
+ unset_attribute(curr,finemathattr)
+ elseif curr.id == glyphnode then
+ emsize = get_font_emsize(curr.font)
+ local uni = get_unicode_char(curr)
+ if uni then
+ prevchar,prevfont = cjk_insert_nodes(head,curr,uni,curr.font,prevchar,prevfont)
+ end
+ unset_attribute(curr,finemathattr)
+ elseif curr.id == mathnode then
+ local currchar = 0
+ if curr.surround and curr.surround > 0 then
+ currchar = 0x4E00
+ end
+ if curr.subtype == 0 then
+ cjk_insert_nodes(head,curr,currchar,nil,prevchar,prevfont)
+ prevchar,prevfont = nil,nil
+ else
+ prevchar,prevfont = currchar,nil
+ end
+ unset_attribute(curr,finemathattr)
+ elseif curr.id == hlistnode or curr.id == vlistnode then
+ local firstchr, firstfid = get_hlist_class_first(curr)
+ if firstchr then
+ cjk_insert_nodes(head,curr,firstchr,firstfid,prevchar,prevfont)
+ end
+ prevchar,prevfont = get_hlist_class_last(curr,prevchar,prevfont)
+ unset_attribute(curr,finemathattr)
+ end
+ else
+ prevchar,prevfont = 0,nil -- treat \verb as latin character.
+ end
+ end
+end
+
+------------------------------------
+-- remove japanese/chinese spaceskip
+------------------------------------
+local function remove_cj_spaceskip (head)
+ for curr in traverse_id(gluenode,head) do
+ local cjattr = has_attribute(curr,cjtypesetattr)
+ local prv, nxt = curr.prev, curr.next
+ if cjattr and cjattr > 0 and prv and nxt then
+ local prevclass, prevchar, prevfont, nextclass
+ if prv.id == hlistnode or prv.id == vlistnode then
+ prevclass = get_hlist_class_last(prv)
+ else
+ -- what is this strange kern before \text??
+ if prv.id == kernnode and prv.kern == 0 then
+ prv = prv.prev
+ end
+ if prv.id == glyphnode then
+ prevclass = get_cjk_class(get_unicode_char(prv), cjattr)
+ prevchar, prevfont = prv.char, prv.font
+ end
+ end
+ if nxt.id == glyphnode then
+ nextclass = get_cjk_class(get_unicode_char(nxt), cjattr)
+ elseif nxt.id == hlistnode or nxt.id == vlistnode then
+ nextclass = get_hlist_class_first(nxt)
+ end
+ if (prevclass and prevclass < 10) or (nextclass and nextclass < 10) then
+ local subtype = curr.subtype
+ if subtype == 13 then -- do not touch on xspaceskip for now
+ remove_node(head,curr)
+ else -- before \text?? spaceskip is replaced by glue type 0
+ local spec = curr.spec
+ local csp = spec and spec.width
+ local cst = spec and spec.stretch
+ local csh = spec and spec.shrink
+ local fp = get_font_table(prevfont)
+ fp = fp and fp.parameters
+ local sp = fp and fp.space
+ local st = fp and fp.space_stretch
+ local sh = fp and fp.space_shrink
+ sp = sp and tex_round(sp)
+ st = st and tex_round(st)
+ sh = sh and tex_round(sh)
+ if prevchar and prevchar >= 65 and prevchar <= 90 then
+ st = st and mathfloor(st*(999/1000))
+ sh = sh and mathfloor(sh*(1001/1000))
+ end
+ if sp == csp and st == cst and sh == csh then
+ remove_node(head,curr)
+ end
+ end
+ end
+ end
+ end
+end
+
+----------------------------------
+-- compress fullwidth punctuations
+----------------------------------
+local function compress_fullwidth_punctuations (head)
+ for curr in traverse_id(glyphnode,head) do
+ local uni = get_unicode_char(curr)
+ local class = uni and get_cjk_class(uni, has_attribute(curr, cjtypesetattr))
+ local chr = get_font_char(curr.font, curr.char)
+ if chr and class and class >= 1 and class <= 4 then
+ local width = curr.width or 655360
+ emsize = get_font_emsize(curr.font)
+ local ensize = emsize/2
+ local oneoften = emsize/10
+ local bbox = get_char_boundingbox(curr.font, curr.char)
+ if not bbox then return head end --
+ if class == 2 or class == 4 then
+ local wd
+ if get_font_feature(curr.font,'vertical') then
+ wd = ensize<width and ensize-width or 0
+ else
+ wd = (bbox[3] < ensize) and ensize-width or bbox[3]-width+oneoften
+ end
+ if chr.right_protruding then
+ -- kern is a breakpoint if followed by a glue
+ insert_after(head, curr, get_kernnode(wd))
+ else
+ insert_after(head, curr, get_rulenode(wd))
+ end
+ elseif class == 1 then
+ local wd
+ if get_font_feature(curr.font,'vertical') then
+ wd = ensize<width and ensize-width or 0
+ else
+ wd = (width-bbox[1] < ensize) and ensize-width or -bbox[1]+oneoften
+ end
+ if chr.left_protruding then
+ head = insert_before(head, curr, get_kernnode(wd))
+ else
+ head = insert_before(head, curr, get_rulenode(wd))
+ end
+ elseif class == 3 then
+ local lwd, rwd
+ local quarter, thirdquarter, halfwd = ensize/2, ensize*1.5, width/2
+ if get_font_feature(curr.font,'vertical') then
+ rwd = quarter<halfwd and quarter-halfwd or 0
+ lwd = rwd
+ else
+ rwd = (bbox[3] < thirdquarter) and quarter-halfwd or bbox[3]-width
+ lwd = (width-bbox[1] < thirdquarter) and quarter-halfwd or -bbox[1]
+ end
+ if chr.left_protruding then
+ head = insert_before(head, curr, get_kernnode(lwd))
+ else
+ head = insert_before(head, curr, get_rulenode(lwd))
+ end
+ if chr.right_protruding then
+ insert_after (head, curr, get_kernnode(rwd))
+ else
+ insert_after (head, curr, get_rulenode(rwd))
+ end
+ end
+ end
+ end
+ return head -- should be returned!
+end
+
+------------------------------
+-- ruby: pre-linebreak routine
+------------------------------
+local function spread_ruby_box(head,extrawidth)
+ for curr in traverse_id(gluenode,head) do
+ if curr.spec then
+ if has_attribute(curr,luakoglueattr) then
+ curr.spec.width = curr.spec.width + extrawidth
+ else
+ head = insert_before(head,curr,get_kernnode(curr.spec.width+extrawidth))
+ head = remove_node(head,curr)
+ end
+ end
+ end
+ return head
+end
+
+local function spread_ruby_base_box (head)
+ for curr in traverse_id(hlistnode,head) do
+ local attr = has_attribute(curr,luakorubyattr)
+ local rubyoverlap = attr and rubynode[attr][3]
+ if attr and not rubyoverlap then
+ local ruby = rubynode[attr][1]
+ if ruby and ruby.width > curr.width then
+ local basehead = curr.head
+ local numofglues = nodecount(gluenode,basehead)
+ local extrawidth = (ruby.width - curr.width)/(numofglues + 1)
+ if numofglues > 0 then
+ basehead = spread_ruby_box(basehead,extrawidth)
+ end
+ local leading = get_kernnode(extrawidth/2)
+ curr.width = ruby.width
+ leading.next = basehead
+ curr.head = leading
+ end
+ end
+ end
+end
+
+local function get_ruby_side_width (basewidth,rubywidth,adjacent)
+ local width,margin = (rubywidth-basewidth)/2, 0
+ if adjacent then
+ if adjacent.id == glyphnode then
+ if not is_hanja(get_unicode_char(adjacent)) then
+ width = (rubywidth-basewidth-emsize)/2
+ if width > 0 then
+ margin = emsize/2
+ else
+ width,margin = 0,(rubywidth-basewidth)/2
+ end
+ end
+ end
+ end
+ return width,margin
+end
+
+local function get_ruby_side_kern (head)
+ for curr in traverse_id(hlistnode,head) do
+ local attr = has_attribute(curr,luakorubyattr)
+ local rubyoverlap = attr and rubynode[attr][3]
+ if rubyoverlap then
+ local basewidth = curr.width
+ local rubywidth = rubynode[attr][1].width
+ if curr.width < rubywidth then
+ local _,fid = get_hlist_class_first(curr)
+ emsize = get_font_emsize(fid)
+ local leftwidth,leftmargin = get_ruby_side_width(basewidth,rubywidth,curr.prev)
+ if leftwidth > 0 then
+ curr.width = curr.width + leftwidth
+ end
+ if leftmargin > 0 then
+ rubynode[attr].leftmargin = leftmargin
+ end
+ local rightwidth,rightmargin = get_ruby_side_width(basewidth,rubywidth,curr.next)
+ if rightwidth > 0 then
+ curr.width = curr.width + rightwidth
+ end
+ if rightmargin > 0 then
+ rubynode[attr].rightmargin = rightmargin
+ end
+ rubynode[attr].rightshift = rightwidth - leftwidth
+ local totalspace = leftwidth+rightwidth
+ if totalspace > 0 then
+ local numofglues = nodecount(gluenode,curr.head)
+ local extrawidth = totalspace/(numofglues + 1)
+ if numofglues > 0 then
+ curr.head = spread_ruby_box(curr.head,extrawidth)
+ end
+ local leading = get_kernnode(extrawidth*(leftwidth/totalspace))
+ leading.next = curr.head
+ curr.head = leading
+ end
+ end
+ end
+ end
+end
+
+local function zero_width_rule_with_dir (head,curr,before)
+ local rule = get_rulenode(0)
+ rule.dir = curr.dir
+ if before then
+ head = insert_before(head,curr,rule)
+ else
+ insert_after(head,curr,rule)
+ end
+ return head
+end
+
+local function no_ruby_at_margin(head)
+ for curr in traverse_id(hlistnode,head) do
+ local attr = has_attribute(curr,luakorubyattr)
+ if attr then
+ local margin = rubynode[attr].leftmargin
+ if margin then
+ head = insert_before(head,curr,get_kernnode(-margin))
+ head = zero_width_rule_with_dir(head,curr,true) -- before
+ head = insert_before(head,curr,get_kernnode(margin))
+ end
+ margin = rubynode[attr].rightmargin
+ if margin then
+ if curr.next then
+ if curr.next.id == gluenode then
+ insert_after(head,curr,get_kernnode(-margin))
+ head = zero_width_rule_with_dir(head,curr)
+ insert_after(head,curr,get_kernnode(margin))
+ elseif curr.next.id == penaltynode and curr.next.penalty < 10000 then
+ insert_after(head,curr.next,get_kernnode(-margin))
+ head = zero_width_rule_with_dir(head,curr)
+ insert_after(head,curr,get_kernnode(margin))
+ end
+ end
+ end
+ end
+ end
+ return head
+end
+
+------------------------------
+-- discourage character orphan
+------------------------------
+local function inject_char_widow_penalty (head,curr,uni,pv,cjattr)
+ if uni and prebreakpenalty[uni] ~= 10000 then
+ local class = get_cjk_class(uni, cjattr)
+ if class and class < 9 then
+ if curr.prev and curr.prev.id == rulenode then
+ curr = curr.prev
+ end
+ if curr.prev and curr.prev.id == gluenode then
+ curr = curr.prev
+ end
+ if curr.prev and curr.prev.id == penaltynode then
+ if curr.prev.penalty < pv then
+ curr.prev.penalty = pv
+ end
+ else
+ insert_before(head,curr,get_penaltynode(pv))
+ end
+ return true
+ end
+ end
+end
+
+local function discourage_char_widow (head,curr)
+ while curr do
+ local cjattr = has_attribute(curr,cjtypesetattr)
+ local pv = cjattr and 500 or 5000
+ if curr.id == glyphnode then
+ emsize = get_font_emsize(curr.font)
+ local width = curr.width or 0
+ if width >= 2*emsize then return end
+ local uni = get_unicode_char(curr)
+ if inject_char_widow_penalty(head,curr,uni,pv,cjattr) then
+ return true
+ end
+ elseif curr.id == hlistnode and curr.id == vlistnode then
+ local width = curr.width or 0
+ local uni,fid = get_hlist_class_first(curr)
+ emsize = get_font_emsize(fid)
+ if width >= 2*emsize then return end
+ if inject_char_widow_penalty(head,curr,uni,pv,cjattr) then
+ return true
+ end
+ end
+ if not curr.prev then return end
+ curr = curr.prev
+ end
+end
+
+---------------------------
+-- automatic josa selection
+---------------------------
+local function syllable2jamo (code)
+ local code = code - 0xAC00
+ local L = 0x1100 + mathfloor(code / 588)
+ local V = 0x1161 + mathfloor((code % 588) / 28)
+ local T = 0x11A7 + code % 28
+ if T == 0x11A7 then T = nil end
+ return L, V, T
+end
+
+local function number2josacode (n)
+ n = n % 10 + stringbyte("0")
+ if josa_code[n] then return josa_code[n] end
+ return nil -- 2
+end
+
+local function latin2josacode (n)
+ n = n + stringbyte("a")
+ if josa_code[n] then return josa_code[n] end
+ return nil -- 2
+end
+
+for c = 0x2160, 0x216B do -- Ⅰ
+ josa_code[c] = number2josacode(c - 0x215F)
+end
+for c = 0x2170, 0x217B do -- ⅰ
+ josa_code[c] = number2josacode(c - 0x216F)
+end
+for c = 0x2460, 0x2473 do -- ①
+ josa_code[c] = number2josacode(c - 0x245F)
+end
+for c = 0x2474, 0x2487 do -- ⑴
+ josa_code[c] = number2josacode(c - 0x2473)
+end
+for c = 0x2488, 0x249B do -- ⒈
+ josa_code[c] = number2josacode(c - 0x2487)
+end
+for c = 0x249C, 0x24B5 do -- ⒜
+ josa_code[c] = latin2josacode(c - 0x249C)
+end
+for c = 0x24B6, 0x24CF do -- Ⓐ
+ josa_code[c] = latin2josacode(c - 0x24B6)
+end
+for c = 0x24D0, 0x24E9 do -- ⓐ
+ josa_code[c] = latin2josacode(c - 0x24D0)
+end
+for c = 0x3131, 0x314E do -- ㄱ
+ josa_code[c] = 3
+end
+josa_code[0x3139] = 1 -- ㄹ
+for c = 0x3165, 0x3186 do
+ josa_code[c] = 3
+end
+for c = 0x3200, 0x320D do -- ㈀
+ josa_code[c] = 3
+end
+josa_code[0x3203] = 1 -- ㈃
+for c = 0x3260, 0x327F do -- ㉠
+ josa_code[c] = 3
+end
+josa_code[0x3263] = 1 -- ㉣
+for c = 0xFF10, 0xFF19 do -- 0
+ josa_code[c] = number2josacode(c - 0xFF10)
+end
+for c = 0xFF21, 0xFF3A do -- A
+ josa_code[c] = latin2josacode(c - 0xFF21)
+end
+for c = 0xFF41, 0xFF5A do -- a
+ josa_code[c] = latin2josacode(c - 0xFF41)
+end
+
+local function jamo2josacode(code)
+ if code and code > 0x11A7 then
+ if code == 0x11AF then return 1 end
+ return 3
+ end
+ return 2
+end
+
+local function get_hanja_hangul_table (table,file,init)
+ local i = 0
+ local file = kpse.find_file(file)
+ if not file then return table end
+ file = io.open(file, "r")
+ if not file then return table end
+ while true do
+ local d = file:read("*number")
+ if not d then break end
+ table[init + i] = d
+ i = i + 1
+ end
+ file:close()
+ return table
+end
+
+local hanja2hangul = { }
+hanja2hangul = get_hanja_hangul_table(hanja2hangul,"hanja_hangul.tab",0x4E00)
+hanja2hangul = get_hanja_hangul_table(hanja2hangul,"hanjaexa_hangul.tab",0x3400)
+hanja2hangul = get_hanja_hangul_table(hanja2hangul,"hanjacom_hangul.tab",0xF900)
+
+-- 1 : 리을, 2 : 중성, 3 : 종성
+local function get_josacode (prevs)
+ local code = prevs[1] -- last char
+ if not code then return 2 end
+ if is_hangul(code) then -- hangul syllable
+ local _, _, T = syllable2jamo(code)
+ return jamo2josacode(T)
+ end
+ if is_jungjongsong(code) then return jamo2josacode(code) end
+ if (code >= 0x3400 and code <= 0x9FA5)
+ or (code >= 0xF900 and code <= 0xFA2D) then
+ local _, _, T = syllable2jamo(hanja2hangul[code])
+ return jamo2josacode(T)
+ end
+ -- latin
+ if prevs[1] < 0x80
+ and prevs[2] and prevs[2] < 0x80
+ and prevs[3] and prevs[3] < 0x80 then
+ local liii = stringchar(prevs[3], prevs[2], prevs[1])
+ if josa_code[liii] then return josa_code[liii] end
+ end
+ if prevs[1] < 0x80
+ and prevs[2] and prevs[2] < 0x80 then
+ local lii = stringchar(prevs[2], prevs[1])
+ if josa_code[lii] then return josa_code[lii] end
+ end
+ if josa_code[code] then return josa_code[code] end
+ return 2
+end
+
+local function get_josaprevs(curr,josaprev)
+ while curr do
+ if curr.id == glyphnode then
+ local chr = get_unicode_char(curr)
+ if xspcode[chr]
+ or inhibitxspcode[chr]
+ or prebreakpenalty[chr]
+ or postbreakpenalty[chr]
+ or chr == 0x302E -- tone mark
+ or chr == 0x302F then -- tone mark
+ --skip
+ else
+ josaprev[#josaprev + 1] = chr
+ end
+ elseif curr.id == hlistnode or curr.id == vlistnode then
+ josaprev = get_josaprevs(nodeslide(curr.head),josaprev)
+ end
+ if #josaprev == 3 then break end
+ curr = curr.prev
+ end
+ return josaprev
+end
+
+local function korean_autojosa (head)
+ for curr in traverse_id(glyphnode,head) do
+ if has_attribute(curr,autojosaattr) and has_attribute(curr,finemathattr) then
+ local josaprev = {}
+ josaprev = get_josaprevs(curr.prev,josaprev)
+ local josacode = get_josacode(josaprev)
+ local thischar = get_unicode_char(curr)
+ if thischar == 0xC774 then
+ if curr.next
+ and curr.next.id == glyphnode
+ and get_unicode_char(curr.next) == 0xB77C then
+ curr.char = josa_list[0xC774][josacode]
+ else
+ curr.char = josa_list[0xAC00][josacode]
+ end
+ elseif thischar and josa_list[thischar] then
+ curr.char = josa_list[thischar][josacode]
+ end
+ end
+ if curr.char < 0 then remove_node(head,curr) end
+ end
+end
+
+------------------------------
+-- switch to hangul/hanja font
+------------------------------
+local function hangulspaceskip (engfont, hfontid, nglue)
+ local eng = engfont.parameters
+ if not eng then return end
+ local spec = nglue.spec
+ if not spec then return end
+ local gsp, gst, gsh = spec.width, spec.stretch, spec.shrink
+ local esp, est, esh = eng.space, eng.space_stretch, eng.space_shrink
+ esp = esp and tex_round(esp)
+ est = est and tex_round(est)
+ esh = esh and tex_round(esh)
+ if esp == gsp and est == gst and esh == gsh then else return end
+ local hf = get_font_table(hfontid)
+ if not hf then return end
+ local hp = hf.parameters
+ if not hp then return end
+ local hsp,hst,hsh = hp.space,hp.space_stretch,hp.space_shrink
+ if hsp and hst and hsh then else return end
+ return tex_round(hsp), tex_round(hst), tex_round(hsh)
+end
+
+local hangulfontlist = {}
+
+local function font_substitute(head)
+ for curr in traverse_id(glyphnode, head) do
+ local eng = get_font_table(curr.font)
+ local engsize = eng and eng.size
+ local engfontchar = get_font_char(curr.font, curr.char)
+ if not engsize then -- no font table of plain tex cm font
+ engsize = tex_sp(tex.pdffontsize(font.current()))
+ engfontchar = get_cjk_class(curr.char) == 10
+ end
+ if curr.char and not engfontchar then
+ local korid = false
+ local hangul = has_attribute(curr, hangfntattr)
+ local hanja = has_attribute(curr, hanjfntattr)
+ local ftable = {hangul, hanja}
+
+ if hanjafontforhanja then
+ local uni = get_unicode_char(curr)
+ uni = uni and get_cjk_class(uni)
+ if uni and uni < 7 then ftable = {hanja, hangul} end
+ end
+ for _,fallback in ipairs(ftable) do
+ local fkey = fallback and hangulfont[fallback]
+ if fkey then
+ local fname, fsize = stringmatch(fkey, "(.+) at (.+)")
+ if not fname then fname = fkey end
+ if fsize then
+ fsize = tex_sp(fsize)
+ else
+ fsize = engsize
+ end
+ fkey = fname .. "@" .. fsize
+ local fid = hangulfontlist[fkey]
+ if not fid then
+ fid = font_define_func(fname, fsize, font.nextid())
+ if type(fid) == "table" then fid = fontdefine(fid) end
+ hangulfontlist[fkey] = fid
+ end
+ local c = get_font_char(fid, curr.char)
+ if c then
+ korid = true
+ curr.font = fid
+ -- adjust next glue by hangul font space
+ local nxt = curr.next
+ if hangulmain and nxt and nxt.id == gluenode and nxt.subtype and nxt.subtype == 0 then
+ local sp,st,sh = hangulspaceskip(eng, fid, nxt)
+ if sp and st and sh then
+ nxt.spec.width = sp
+ nxt.spec.stretch = st
+ nxt.spec.shrink = sh
+ end
+ end
+ --- charraise option charraise
+ local charraise = get_font_feature(fid, "charraise")
+ if charraise then
+ charraise = tex_sp(charraise)
+ curr.yoffset = curr.yoffset and (curr.yoffset + charraise) or charraise
+ end
+ ---
+ break
+ end
+ end
+ end
+ if not korid then
+ warn("!Missing character: %s U+%04X", utf8char(curr.char),curr.char)
+ end
+ end
+ end
+ return head
+end
+
+-----------------------------
+-- reserve unicode code point
+-----------------------------
+local function assign_unicode_codevalue (head)
+ for curr in traverse_id(glyphnode, head) do
+ set_attribute(curr, luakounicodeattr, curr.char)
+ end
+ return head
+end
+
+
+------------------------------------
+-- vetical typesetting: EXPERIMENTAL -- don't use this
+------------------------------------
+---[[no vwidth in luaotfload v2
+local tsbtable = {}
+
+local function read_tsb_table(filename)-- for ttx-generated vmtx table
+ if tsbtable[filename] then
+ return tsbtable[filename]
+ end
+ local filepath = kpse.find_file(filename)
+ if not filepath then return end
+ local file = io.open(filepath, "r")
+ if not file then return end
+ local tsbtb = {}
+ local patt = 'name="(.-)" height="(.-)" tsb="(.-)"'
+ while true do
+ local l = file:read("*line")
+ if not l then break end
+ for name, height, tsb in stringgmatch(l,patt) do
+ tsbtb[name] = {}
+ tsbtb[name].height = height
+ tsbtb[name].tsb = tsb
+ end
+ end
+ file:close()
+ tsbtable[filename] = tsbtb
+ return tsbtb
+end
+
+local function cjk_vertical_font (vf)
+ if not vf.shared then return end
+ if not vf.shared.features then return end
+ if not vf.shared.features["vertical"] then return end
+ if vf.type == "virtual" then return end
+
+ --- for read-ttx
+ local filename = vf.filename
+ filename = stringgsub(filename,".*/","")
+ filename = stringgsub(filename,"[tToO][tT][fF]$","ttx")
+ local tsbtable = read_tsb_table(filename)
+ if not tsbtable then
+ warn("Cannot read %s. Aborting vertical typesetting.",filename)
+ return
+ end
+ ---
+
+ local tmp = table.copy(vf) -- fastcopy takes time too long.
+ local id = fontdefine(tmp)
+
+ local hash = vf.properties and vf.properties.hash and vf.properties.hash..' @ vertical'
+ hash = hash or (vf.name and vf.size and vf.name..' @ '..vf.size..' @ vertical')
+
+ vf.properties = vf.properties or {}
+ vf.properties.hash = vf.properties.hash or ""
+ vf.properties.hash = hash
+ vf.type = 'virtual'
+ vf.fonts = {{ id = id }}
+ local quad = vf.parameters and vf.parameters.quad or 655360
+ local descriptions = vf.shared and vf.shared.rawdata and vf.shared.rawdata.descriptions
+ local ascender = vf.parameters and vf.parameters.ascender
+ local factor = vf.parameters and vf.parameters.factor
+ local halfxht = (vf.parameters and vf.parameters.x_height and vf.parameters.x_height/2) or quad/4
+ for i,v in pairs(vf.characters) do
+ local dsc = descriptions[i]
+ local gname = dsc.name
+ -- local vw = dsc and dsc.vwidth
+ --- for read-ttx
+ local vw = tsbtable and tsbtable[gname] and tsbtable[gname].height
+ local tsb = tsbtable and tsbtable[gname] and tsbtable[gname].tsb
+ if not vw and dsc.index then
+ local cid = stringformat("cid%05d", dsc.index)
+ vw = tsbtable and tsbtable[cid] and tsbtable[cid].height
+ tsb = tsbtable and tsbtable[cid] and tsbtable[cid].tsb
+ end
+ tsb = tsb and factor and tsb*factor
+ ---
+ vw = vw and factor and vw * factor
+ vw = vw or quad
+ local vh = dsc and dsc.boundingbox and dsc.boundingbox[3]
+ vh = vh and factor and vh * factor
+ vh = vh and vh - quad/2 or quad/2
+ vh = vh + halfxht
+ vh = vh > 0 and vh or nil
+ local vd = dsc and dsc.boundingbox and dsc.boundingbox[1]
+ vd = vd and factor and vd * factor
+ vd = vd and quad/2 - vd or quad/2
+ vd = vd - halfxht
+ vd = vd > 0 and vd or nil
+ local bb4 = dsc and dsc.boundingbox and dsc.boundingbox[4]
+ bb4 = bb4 and factor and bb4*factor
+ local asc = bb4 and tsb and bb4 + tsb
+ asc = asc or ascender
+ v.commands = {
+ {'right', asc}, -- bbox4 + top_side_bearing! But, tsb not available!
+ {'down', halfxht},
+ {'special', 'pdf: q 0 1 -1 0 0 0 cm'},
+ {'push'},
+ {'char', i},
+ {'pop'},
+ {'special', 'pdf: Q'},
+ }
+ v.width = vw
+ v.height = vh
+ v.depth = vd
+ end
+ return vf
+end
+
+local function activate_vertical_virtual (tfmdata,value)
+ if value then
+ add_to_callback("luaotfload.patch_font",
+ cjk_vertical_font,
+ "luatexko.vetical_virtual_font")
+ end
+end
+
+local otffeatures = fonts.constructors.newfeatures("otf")
+otffeatures.register {
+ name = "vertical",
+ description = "vertical typesetting",
+ initializers = {
+ node = activate_vertical_virtual,
+ }
+}
+--no vwidth in luaotfload v2]]
+
+----------------------------------
+-- add to callback : pre-linebreak
+----------------------------------
+add_to_callback('hpack_filter', function(head)
+ assign_unicode_codevalue(head)
+ korean_autojosa(head)
+ remove_cj_spaceskip(head)
+ font_substitute(head)
+ return head
+end, 'luatexko.hpack_filter_first',1)
+
+add_to_callback('hpack_filter', function(head)
+ get_ruby_side_kern(head)
+ cjk_spacing_linebreak(head)
+ spread_ruby_base_box(head)
+ head = compress_fullwidth_punctuations(head)
+ -- head = no_ruby_at_margin(head)
+ return head
+end, 'luatexko.hpack_filter')
+
+add_to_callback('pre_linebreak_filter', function(head)
+ assign_unicode_codevalue(head)
+ korean_autojosa(head)
+ remove_cj_spaceskip(head)
+ font_substitute(head)
+ return head
+end, 'luatexko.pre_linebreak_filter_first',1)
+
+add_to_callback('pre_linebreak_filter', function(head)
+ get_ruby_side_kern(head)
+ cjk_spacing_linebreak(head)
+ spread_ruby_base_box(head)
+ head = compress_fullwidth_punctuations(head)
+ discourage_char_widow(head, nodeslide(head))
+ head = no_ruby_at_margin(head)
+ return head
+end, 'luatexko.pre_linebreak_filter')
+
+
+--------------------------
+-- dot emphasis (드러냄표)
+--------------------------
+local function after_linebreak_dotemph (head)
+ for curr in traverse(head) do
+ if curr.id == hlistnode then -- hlist may be nested!!!
+ after_linebreak_dotemph(curr.head)
+ elseif curr.id == glyphnode then
+ local attr = has_attribute(curr,dotemphattr)
+ if attr and attr > 0 then
+ local d = copy_node(dotemphnode[attr])
+ local dot = d.head
+ d.head = get_kernnode(-curr.width/2-d.width/2)
+ d.head.next = dot
+ d.width = 0
+ insert_after(head,curr,d)
+ unset_attribute(curr,dotemphattr)
+ end
+ end
+ end
+end
+
+-------------------------------
+-- ruby: post-linebreak routine
+-------------------------------
+local function after_linebreak_ruby (head)
+ for curr in traverse_id(hlistnode,head) do
+ after_linebreak_ruby(curr.head) -- hlist may be nested!!!
+ local attr = has_attribute(curr,luakorubyattr)
+ if attr then
+ local ruby = rubynode[attr][1]
+ if ruby then
+ if ruby.width < curr.width then
+ local rubyhead = ruby.head
+ local numofglues = nodecount(gluenode,rubyhead)
+ local extrawidth = (curr.width - ruby.width)/(numofglues + 1)
+ ruby.width = curr.width - extrawidth/2
+ if numofglues > 0 then
+ ruby.head = spread_ruby_box(rubyhead,extrawidth)
+ end
+ else
+ local right = rubynode[attr].rightshift or 0
+ ruby.width = curr.width + (ruby.width-curr.width+right)/2
+ end
+ ruby.shift = -curr.height-rubynode[attr][2]
+ insert_after(head,curr,ruby)
+ insert_after(head,curr,get_kernnode(-ruby.width))
+ end
+ rubynode[attr] = nil
+ unset_attribute(curr,luakorubyattr)
+ end
+ end
+end
+
+---------------------
+-- underline emphasis
+---------------------
+local function draw_underline(head,curr,width,ulinenum,ulstart)
+ if width and width > 0 then
+ local glue = get_gluenode(width)
+ glue.subtype = 101 -- cleaders
+ glue.leader = copy_node(ulinebox[ulinenum])
+ insert_before(head, curr, get_kernnode(-width))
+ insert_before(head, curr, glue)
+ end
+ for _,nd in ipairs({ulstart,curr}) do
+ if nd.id == whatsitnode
+ and nd.subtype == whatsitspecial
+ and nd.data
+ and stringfind(nd.data,"luako:uline") then
+ remove_node(head,nd)
+ end
+ end
+end
+
+local function after_linebreak_underline(head,glueorder,glueset,gluesign,ulinenum)
+ local ulstart = ulinenum and head or false
+ if ulstart and ulstart.id == gluenode then ulstart = ulstart.next end
+ for curr in traverse(head) do
+ if curr.id == hlistnode then
+ ulinenum = after_linebreak_underline(curr.head,curr.glue_order,curr.glue_set,curr.glue_sign,ulinenum)
+ elseif curr.id == whatsitnode and curr.subtype == whatsitspecial
+ and curr.data then
+ if stringfind(curr.data,"luako:ulinebegin=") then
+ ulinenum = tonumber(stringmatch(curr.data,"(%d+)"))
+ ulstart = curr
+ elseif ulstart and ulinenum
+ and stringfind(curr.data,'luako:ulineend') then
+ local wd = nodedimensions(glueset,gluesign,glueorder,ulstart,curr)
+ draw_underline(head,curr,wd,ulinenum,ulstart)
+ ulinebox[ulinenum] = nil
+ ulinenum = nil
+ end
+ end
+ if ulstart and ulinenum and curr == nodetail(head) then
+ local wd = nodedimensions(glueset,gluesign,glueorder,ulstart,curr)
+ draw_underline(head,curr,wd,ulinenum,ulstart)
+ end
+ end
+ return ulinenum
+end
+
+-----------------------------------
+-- add to callback : post-linebreak
+-----------------------------------
+add_to_callback('vpack_filter', function(head)
+ after_linebreak_dotemph(head)
+ after_linebreak_ruby(head)
+ after_linebreak_underline(head)
+ return true
+end, 'luatexko.vpack_filter')
+
+add_to_callback("post_linebreak_filter", function(head)
+ after_linebreak_dotemph(head)
+ after_linebreak_ruby(head)
+ after_linebreak_underline(head)
+ return true
+end, 'luatexko.post_linebreak_filter')
+