local zhspuncs = zhspuncs or {} local hlist = nodes.nodecodes.hlist local glyph = nodes.nodecodes.glyph --node.id ('glyph') local fonthashes = fonts.hashes local fontdata = fonthashes.identifiers local quaddata = fonthashes.quads local node_count = node.count local node_dimensions = node.dimensions local node_traverse_id = node.traverseid or node.traverse_id -- for mkiv local insert_before = node.insertbefore or node.insert_before -- for mkiv local insert_after = node.insertafter or node.insert_after -- for mkiv local new_kern = nodes.pool.kern local tasks = nodes.tasks local left_puncs = { [0x2018] = 0.35, -- ‘ [0x201C] = 0.35, -- “ [0x3008] = 0.35, -- 〈 [0x300A] = 0.35, -- 《 [0x300C] = 0.35, -- 「 [0x300E] = 0.35, -- 『 [0x3010] = 0.35, -- 【 [0x3014] = 0.35, -- 〔 [0x3016] = 0.35, -- 〖 [0xFF08] = 0.35, -- ( [0xFF3B] = 0.35, -- [ [0xFF5B] = 0.35 -- { } local function is_left_punc(n) if left_puncs[n.char] then return true end return false end local puncs = { [0x2018] = {0.5, 0.1, 1.0, 1.0}, -- ‘ [0x201C] = {0.5, 0.1, 0.5, 1.0}, -- “ [0x3008] = {0.5, 0.1, 1.0, 1.0}, -- 〈 [0x300A] = {0.5, 0.1, 1.0, 1.0}, -- 《 [0x300C] = {0.5, 0.1, 1.0, 1.0}, -- 「 [0x300E] = {0.5, 0.1, 1.0, 1.0}, -- 『 [0x3010] = {0.5, 0.1, 1.0, 1.0}, -- 【 [0x3014] = {0.5, 0.1, 1.0, 1.0}, -- 〔 [0x3016] = {0.5, 0.1, 1.0, 1.0}, -- 〖 [0xFF08] = {0.5, 0.1, 1.0, 1.0}, -- ( [0xFF3B] = {0.5, 0.1, 1.0, 1.0}, -- [ [0xFF5B] = {0.5, 0.1, 1.0, 1.0}, -- { [0x2019] = {0.1, 0.5, 1.0, 0.0}, -- ’ [0x201D] = {0.1, 0.5, 1.0, 0.0}, -- ” [0x3009] = {0.1, 0.5, 1.0, 0.5}, -- 〉 [0x300B] = {0.1, 0.5, 1.0, 0.5}, -- 》 [0x300D] = {0.1, 0.5, 1.0, 0.5}, -- 」 [0x300F] = {0.1, 0.5, 1.0, 0.5}, -- 』 [0x3011] = {0.1, 0.5, 1.0, 0.5}, -- 】 [0x3015] = {0.1, 0.5, 1.0, 0.5}, -- 〕 [0x3017] = {0.1, 0.5, 1.0, 0.5}, -- 〗 [0xFF09] = {0.1, 0.5, 1.0, 0.5}, -- ) [0xFF3D] = {0.1, 0.5, 1.0, 0.5}, -- ] [0xFF5D] = {0.1, 0.5, 1.0, 0.5}, -- } -- 需要特殊处理 [0x2014] = {0.0, 0.0, 1.0, 1.0}, -- — [0x2026] = {0.1, 0.1, 1.0, 1.0}, -- … [0x2500] = {0.0, 0.0, 1.0, 1.0}, -- ─ [0x3001] = {0.15, 0.5, 1.0, 0.5}, -- 、 [0x3002] = {0.15, 0.6, 1.0, 0.3}, -- 。 [0xFF01] = {0.15, 0.5, 1.0, 0.5}, -- ! [0xFF05] = {0.0, 0.0, 1.0, 0.5}, -- % [0xFF0C] = {0.15, 0.5, 1.0, 0.3}, -- , [0xFF0E] = {0.15, 0.5, 1.0, 0.5}, -- . [0xFF1A] = {0.15, 0.5, 1.0, -0.1}, -- : [0xFF1B] = {0.15, 0.5, 1.0, 0.5}, -- ; [0xFF1F] = {0.15, 0.5, 1.0, 0.5}, -- ? } local function is_zhcnpunc_node_group (n) local n_is_punc = 0 if puncs[n.char] then n_is_punc = 1 end local nn = n.next local nn_is_punc = 0 -- 还需要穿越那些非 glyph 结点 while nn_is_punc == 0 and nn and n_is_punc == 1 do if nn.id == glyph then if puncs[nn.char] then nn_is_punc = 1 end break end nn = nn.next end return n_is_punc + nn_is_punc end local function is_cjk_ideo (n) -- CJK Ext A if n.char >= 13312 and n.char <= 19893 then return true -- CJK elseif n.char >= 19968 and n.char <= 40891 then return true -- CJK Ext B elseif n.char >= 131072 and n.char <= 173782 then return true else return false end end local function quad_multiple (font, r) local quad = quaddata[font] return r * quad end local function process_punc (head, n, punc_flag, punc_table) local desc = fontdata[n.font].descriptions[n.char] if not desc or not desc.boundingbox then return end local quad = quad_multiple (n.font, 1) local l_space = desc.boundingbox[1] / desc.width local r_space = (desc.width - desc.boundingbox[3]) / desc.width local l_kern, r_kern = 0.0, 0.0 if punc_flag == 1 then l_kern = (punc_table[n.char][1] - l_space) * quad r_kern = (punc_table[n.char][2] - r_space) * quad elseif punc_flag == 2 then l_kern = (punc_table[n.char][1] * punc_table[n.char][3] - l_space) * quad r_kern = (punc_table[n.char][2] * punc_table[n.char][4] - r_space) * quad end insert_before (head, n, new_kern (l_kern)) insert_after (head, n, new_kern (r_kern)) end local function compress_punc (head) for n in node_traverse_id (glyph, head) do local n_flag = is_zhcnpunc_node_group (n) if n_flag ~= 0 then process_punc (head, n, n_flag, puncs) end end end function zhspuncs.my_linebreak_filter (head, is_display) compress_punc (head) return head, true end function zhspuncs.align_left_puncs(head) local it = head while it do -- 仅处理第一行的左标点的伸出 if it.id == hlist then local e = it.head local neg_kern = nil local hit = nil while e do if e.id == glyph then if is_left_punc(e) then hit = e end break end e = e.next end if hit ~= nil then -- 文本行整体向左偏移 --neg_kern = -left_puncs[hit.char] * quad_multiple(hit.font, 1) neg_kern = -0.5 * quad_multiple(hit.font, 1) insert_before(head, hit, new_kern(neg_kern)) -- 统计字符个数 local w = 0 local x = hit while x do if x.id == glyph then w = w + 1 end x = x.next end if w == 0 then w = 1 end -- 将 neg_kern 分摊出去 x = it.head -- 重新遍历 av_neg_kern = -neg_kern/w local i = 0 while x do if x.id == glyph then i = i + 1 -- 最后一个字符之后不插入 kern if i < w then insert_after(head, x, new_kern(av_neg_kern)) end end x = x.next end end end it = it.next end return head, done end function zhspuncs.opt () tasks.appendaction("processors","after","zhspuncs.my_linebreak_filter") tasks.appendaction("finalizers", "after", "zhspuncs.align_left_puncs") end -- for '\definefontfeature[whatever][default][mode=node,protrusion=zhspuncs]' -- reference: ConTeXt offical 'font-imp-quality.lua' fonts.protrusions.classes["zhspuncs"] = { vector = "myvector", factor = 1 } fonts.protrusions.vectors["myvector"] = { [0xFF0c] = { 0, 0.60 }, -- , [0x3002] = { 0, 0.60 }, -- 。 [0x2018] = { 0.60, 0 }, -- ‘ [0x2019] = { 0, 0.60 }, -- ’ [0x201C] = { 0.50, 0 }, -- “ [0x201D] = { 0, 0.50 }, -- ” [0xFF1F] = { 0, 0.60 }, -- ? [0x300A] = { 0.60, 0 }, -- 《 [0x300B] = { 0, 0.60 }, -- 》 [0xFF08] = { 0.50, 0 }, -- ( [0xFF09] = { 0, 0.50 }, -- ) [0x3001] = { 0, 0.50 }, -- 、 [0xFF0E] = { 0, 0.50 }, -- . } return zhspuncs