diff options
Diffstat (limited to 'Master/texmf-dist/tex/luatex/luatexko/luatexko.lua')
-rw-r--r-- | Master/texmf-dist/tex/luatex/luatexko/luatexko.lua | 524 |
1 files changed, 317 insertions, 207 deletions
diff --git a/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua b/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua index 8ef25e1f439..aa14bbc249f 100644 --- a/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua +++ b/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua @@ -1,6 +1,6 @@ -- luatexko.lua -- --- Copyright (c) 2013 Dohyun Kim <nomos at ktug org> +-- Copyright (c) 2013-2014 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 @@ -12,8 +12,8 @@ local err,warn,info,log = luatexbase.provides_module({ name = 'luatexko', - date = '2014/01/06', - version = 1.4, + date = '2014/05/11', + version = 1.5, description = 'Korean linebreaking and font-switching', author = 'Dohyun Kim', license = 'LPPL v1.3+', @@ -26,21 +26,21 @@ local dotemphnode,rubynode,ulinebox = {},{},{} luatexko.dotemphnode = dotemphnode luatexko.rubynode = rubynode luatexko.ulinebox = ulinebox -luatexko.hanjafontforhanja = luatexko.hanjafontforhanja or false 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 string_sub = string.sub local mathfloor = math.floor local tex_round = tex.round local tex_sp = tex.sp +local texcount = tex.count local fontdefine = font.define -local utf8char = unicode.utf8.char + +local kpse_find_file = kpse.find_file local fontdata = fonts.hashes.identifiers @@ -57,14 +57,17 @@ local nodecount = node.count local nodeslide = node.slide local nodedimensions = node.dimensions local nodetail = node.tail +local end_of_math = node.end_of_math +local nodenext = node.next +local nodeprev = node.prev 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 hangulfntattr = luatexbase.attributes.hangulfntattr +local hanjafntattr = luatexbase.attributes.hanjafntattr local fallbackfntattr = luatexbase.attributes.fallbackfntattr local hangulpunctsattr = luatexbase.attributes.hangulpunctsattr local luakoglueattr = luatexbase.new_attribute("luakoglueattr") @@ -552,7 +555,6 @@ local function get_cjk_class (ch, cjtype) end return c end - return nil end local function get_font_table (fid) @@ -597,8 +599,8 @@ end local function get_unicode_char(curr) local uni = curr.char - if (uni > 0 and uni < 0xE000) or (uni > 0xF8FF and uni < 0xF0000) then - return uni -- no pua + if (uni > 0xFF and uni < 0xE000) or (uni > 0xF8FF and uni < 0xF0000) then + return uni -- no pua. no nanumgtm?? end -- tounicode is now reliable. backend is fixed uni = get_font_char(curr.font, curr.char) @@ -622,7 +624,7 @@ local function get_hlist_class_first (hlist) elseif curr.id == gluenode then if curr.spec and curr.spec.width ~= 0 then return end end - curr = curr.next + curr = nodenext(curr) end end @@ -638,7 +640,7 @@ local function get_hlist_class_last (hlist,prevchar,prevfont) elseif curr.id == gluenode then if curr.spec and curr.spec.width ~= 0 then return end end - curr = curr.prev + curr = nodeprev(curr) end return prevchar, prevfont end @@ -679,7 +681,7 @@ end local function koreanlatinskip (head,curr,currfont,prevfont,was_penalty) local width = 0 -- default: 0em - if has_attribute(curr,finemathattr) == 1 then -- not ttfamily + if (has_attribute(curr,finemathattr) or 0) > 0 then -- not ttfamily local latincjk = get_font_feature(currfont, "interlatincjk") if not latincjk then latincjk = get_font_feature(prevfont, "interlatincjk") @@ -700,8 +702,8 @@ local function cjk_insert_nodes(head,curr,currchar,currfont,prevchar,prevfont) 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 and c < 10 then -- not ttfamily - local nn, raise = curr.next, nil + if curr.id == glyphnode and (has_attribute(curr,finemathattr) or 0) > 0 and c < 10 then -- not ttfamily + local nn, raise = nodenext(curr), nil while nn do if nn.id == glyphnode and latin_fullstop[nn.char] then if not raise then @@ -709,12 +711,11 @@ local function cjk_insert_nodes(head,curr,currchar,currfont,prevchar,prevfont) raise = raise and tex_sp(raise) end if raise then - nn.yoffset = nn.yoffset or 0 - nn.yoffset = nn.yoffset + raise + nn.yoffset = (nn.yoffset or 0) + raise end - nn = nn.next + nn = nodenext(nn) elseif nn.id == kernnode then - nn = nn.next + nn = nodenext(nn) else break end @@ -796,7 +797,8 @@ end local function cjk_spacing_linebreak (head) local prevchar,prevfont = nil,nil - for curr in traverse(head) do + local curr = head + while curr do if has_attribute(curr,finemathattr) then if curr.id == gluenode then prevchar,prevfont = nil,nil @@ -815,8 +817,8 @@ local function cjk_spacing_linebreak (head) end if curr.subtype == 0 then cjk_insert_nodes(head,curr,currchar,nil,prevchar,prevfont) - prevchar,prevfont = nil,nil - else + curr = end_of_math(curr) + if not curr then break end prevchar,prevfont = currchar,nil end unset_attribute(curr,finemathattr) @@ -831,6 +833,7 @@ local function cjk_spacing_linebreak (head) else prevchar,prevfont = 0,nil -- treat \verb as latin character. end + curr = nodenext(curr) end end @@ -838,55 +841,61 @@ 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 + local curr, prevfont = head, nil + while curr do + if curr.id == mathnode and curr.subtype == 0 then + curr = end_of_math(curr) + elseif curr.id == gluenode then + local cjattr = has_attribute(curr,cjtypesetattr) + local prv, nxt = nodeprev(curr), nodenext(curr) + if cjattr and cjattr > 0 and prv and nxt then + local prevclass, prevchar, 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 = nodeprev(prv) + end + if prv.id == glyphnode then + prevclass = get_cjk_class(get_unicode_char(prv), cjattr) + prevchar, prevfont = prv.char, prv.font + end end - if prv.id == glyphnode then - prevclass = get_cjk_class(get_unicode_char(prv), cjattr) - prevchar, prevfont = prv.char, prv.font + 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 - 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 + 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 + curr = nodenext(curr) end end @@ -906,7 +915,7 @@ local function compress_fullwidth_punctuations (head) local ensize = emsize/2 local oneoften = emsize/10 local bbox = get_char_boundingbox(curr.font, curr.char) - if not bbox then return head end -- + bbox = bbox or {ensize-oneoften, ensize-oneoften, ensize+oneoften, ensize+oneoften} if class == 2 or class == 4 then local wd if get_font_feature(curr.font,'vertical') then @@ -1025,14 +1034,14 @@ local function get_ruby_side_kern (head) 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) + local leftwidth,leftmargin = get_ruby_side_width(basewidth,rubywidth,nodeprev(curr)) 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) + local rightwidth,rightmargin = get_ruby_side_width(basewidth,rubywidth,nodenext(curr)) if rightwidth > 0 then curr.width = curr.width + rightwidth end @@ -1079,13 +1088,14 @@ local function no_ruby_at_margin(head) end margin = rubynode[attr].rightmargin if margin then - if curr.next then - if curr.next.id == gluenode then + local nn = nodenext(curr) + if nn then + if nn.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)) + elseif nn.id == penaltynode and nn.penalty < 10000 then + insert_after(head,nn,get_kernnode(-margin)) head = zero_width_rule_with_dir(head,curr) insert_after(head,curr,get_kernnode(margin)) end @@ -1104,14 +1114,15 @@ local function inject_char_widow_penalty (head,curr,uni,cjattr) local class = get_cjk_class(uni, cjattr) if class and class < 9 then local pv = cjattr and 500 or 5000 - if curr.prev and curr.prev.id == rulenode then - curr = curr.prev + local np = nodeprev(curr) + if np and np.id == rulenode then + curr = np; np = nodeprev(curr) end - if curr.prev and curr.prev.id == gluenode then - curr = curr.prev + if np and np.id == gluenode then + curr = np; np = nodeprev(curr) end - if curr.prev and curr.prev.id == penaltynode then - curr = curr.prev + if np and np.id == penaltynode then + curr = np if curr.penalty < pv then curr.penalty = pv end @@ -1141,7 +1152,7 @@ local function discourage_char_widow (head,curr) local cjattr = has_attribute(curr,cjtypesetattr) curr = inject_char_widow_penalty(head,curr,uni,cjattr) end - curr = curr.prev + curr = nodeprev(curr) end end @@ -1228,7 +1239,7 @@ end local function get_hanja_hangul_table (table,file,init) local i = 0 - local file = kpse.find_file(file) + 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 @@ -1301,7 +1312,7 @@ local function get_josaprevs(curr,josaprev,ignoreparens,halt) josaprev = get_josaprevs(nodeslide(curr.head),josaprev,ignoreparens,halt) end if #josaprev == 3 then break end - curr = curr.prev + curr = nodeprev(curr) end return josaprev end @@ -1311,13 +1322,12 @@ local function korean_autojosa (head) if has_attribute(curr,autojosaattr) and has_attribute(curr,finemathattr) then local ignoreparens = has_attribute(curr,autojosaattr) > 1 and true or false local josaprev = {} - josaprev = get_josaprevs(curr.prev,josaprev,ignoreparens) + josaprev = get_josaprevs(nodeprev(curr),josaprev,ignoreparens) 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 + local nn = nodenext(curr) + if nn and nn.id == glyphnode and get_unicode_char(nn) == 0xB77C then curr.char = josa_list[0xC774][josacode] else curr.char = josa_list[0xAC00][josacode] @@ -1353,30 +1363,65 @@ local function hangulspaceskip (engfont, hfontid, spec) return tex_round(hsp), tex_round(hst), tex_round(hsh) end +local type1fonts = {} -- due to too verbose log +local function nanumtype1font(curr) + if curr.char > 0xFFFF then return end + local fnt_t = get_font_table(curr.font) + local family = (has_attribute(curr,finemathattr) or 0) > 1 and not is_hanja(curr.char) and "nanummj" or "nanumgt" + local series = fnt_t.shared and fnt_t.shared.rawdata.metadata.pfminfo.weight + if series then + series = series > 500 and "b" or "m" + else + series = stringfind(fnt_t.name,"^cmb") and "b" or "m" + end + local shape = fnt_t.parameters.slant > 0 and "o" or "" + local subfnt = stringformat("%s%s%s%02x",family,series,shape,curr.char/256) + local fsize = fnt_t.size or 655360 + local fspec = stringformat("%s@%d",subfnt,fsize) + local newfnt = type1fonts[fspec] + local newchr = curr.char % 256 + local function ital_corr (curr,chr_t) + if shape ~= "o" then return end + local nxt = nodenext(curr) + if nxt and nxt.id == kernnode and nxt.subtype == 1 and nxt.kern == 0 then + nxt.kern = chr_t.italic or 0 + end + end + if newfnt then + local fntchr = get_font_char(newfnt,newchr) + if fntchr then + curr.font, curr.char = newfnt, newchr + ital_corr(curr,fntchr) + end + else + local ft, id = fonts.constructors.readanddefine(subfnt,fsize) + local fntchr = ft and ft.characters[newchr] + if id and fntchr then + type1fonts[fspec], curr.font, curr.char = id, id, newchr + ital_corr(curr,fntchr) + end + end +end + local function font_substitute(head) - local noinmath = true - for curr in traverse(head) do - if curr.id == mathnode then - if curr.subtype == 0 then - noinmath = false - else - noinmath = true - end - elseif noinmath and curr.id == glyphnode then + local curr = head + while curr do + if curr.id == mathnode and curr.subtype == 0 then + curr = end_of_math(curr) + elseif curr.id == glyphnode then local eng = get_font_table(curr.font) - local engfontchar = nil + local myfontchar = nil if eng and eng.encodingbytes and eng.encodingbytes == 2 -- exclude type1 and hangulpunctuations[curr.char] and has_attribute(curr, hangulpunctsattr) - and has_attribute(curr, finemathattr) == 1 + and (has_attribute(curr, finemathattr) or 0) > 0 -- not ttfamily and not get_font_char(curr.font, 0xAC00) then -- exclude hangul font else - engfontchar = get_font_char(curr.font, curr.char) + myfontchar = get_font_char(curr.font, curr.char) end - if curr.char and not engfontchar then - local korid = false - local hangul = has_attribute(curr, hangfntattr) - local hanja = has_attribute(curr, hanjfntattr) + if curr.char and not myfontchar then + local hangul = has_attribute(curr, hangulfntattr) + local hanja = has_attribute(curr, hanjafntattr) local fallback = has_attribute(curr,fallbackfntattr) local ftable = {hangul, hanja, fallback} if luatexko.hanjafontforhanja then @@ -1384,17 +1429,15 @@ local function font_substitute(head) uni = uni and get_cjk_class(uni) if uni and uni < 7 then ftable = {hanja, hangul, fallback} end end - for i=1,#ftable do + for i = 1,3 do local fid = ftable[i] - if fid then - local c = get_font_char(fid, curr.char) - if c then - korid = true - curr.font = fid + myfontchar = get_font_char(fid, curr.char) + if myfontchar then + curr.font = fid + local nxt = nodenext(curr) + if eng and nxt then -- adjust next glue by hangul font space - local nxt = curr.next - if eng - and nxt and nxt.id == gluenode + if nxt.id == gluenode and nxt.subtype and nxt.subtype == 0 and nxt.spec and nxt.spec.writable and get_font_char(fid,32) then @@ -1404,25 +1447,32 @@ local function font_substitute(head) hg.width, hg.stretch, hg.shrink = sp, st, sh nxt.spec = hg end + -- adjust next italic correction kern + elseif nxt.id == kernnode + and nxt.subtype == 1 and nxt.kern == 0 then + local ksl = get_font_table(fid).parameters.slant + if ksl and ksl > 0 then + nxt.kern = myfontchar.italic or 0 + 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 + --- charraise option charraise + local charraise = get_font_feature(fid, "charraise") + if charraise then + charraise = tex_sp(charraise) + curr.yoffset = charraise + (curr.yoffset or 0) + end + --- + break end end - if not korid and not is_unicode_vs(curr.char) then - warn("!Missing character: %s U+%04X", utf8char(curr.char),curr.char) + if not myfontchar then + nanumtype1font(curr) end end end + curr = nodenext(curr) end - return head end ----------------------------- @@ -1440,19 +1490,25 @@ end ----------------------------- local function reorderTM (head) for curr in traverse_id(glyphnode, head) do - if curr.width > 0 then -- old hangul vertical typesetting is broken? - local uni = get_unicode_char(curr) - if uni and (uni == 0x302E or uni == 0x302F) then - local p = curr.prev + local uni = get_unicode_char(curr) + if uni and (uni == 0x302E or uni == 0x302F) then + local unichar = get_font_char(curr.font, uni) + if unichar.width > 0 then + local p = nodeprev(curr) while p do - if p.id ~= glyphnode then break end - local pc = get_cjk_class(get_unicode_char(p)) - if pc == 7 or pc == 8 then - head = insert_before(head,p,copy_node(curr)) - head = remove_node(head,curr) + if p.id == glyphnode then + local pc = get_cjk_class(get_unicode_char(p)) + if pc == 7 or pc == 8 then + head = insert_before(head,p,copy_node(curr)) + head = remove_node(head,curr) + break + end + elseif unichar.commands and p.id == kernnode then + -- kerns in vertical typesetting mode + else break end - p = p.prev + p = nodeprev(p) end end end @@ -1467,7 +1523,7 @@ local function hanja_vs_support (head) for curr in traverse_id(glyphnode, head) do local cc = curr.char if is_unicode_vs(cc) then - local prev = curr.prev + local prev = nodeprev(curr) if prev and prev.id == glyphnode then local f = get_font_table(prev.font) local ivs = f and f.resources and f.resources.variants @@ -1496,9 +1552,9 @@ add_to_callback('hpack_filter', function(head) end, 'luatexko.hpack_filter_first',1) add_to_callback('hpack_filter', function(head) - get_ruby_side_kern(head) + if texcount["luakorubyattrcnt"]>0 then get_ruby_side_kern(head) end cjk_spacing_linebreak(head) - spread_ruby_base_box(head) + if texcount["luakorubyattrcnt"]>0 then spread_ruby_base_box(head) end head = compress_fullwidth_punctuations(head) -- head = no_ruby_at_margin(head) head = reorderTM(head) @@ -1515,12 +1571,12 @@ add_to_callback('pre_linebreak_filter', function(head) end, 'luatexko.pre_linebreak_filter_first',1) add_to_callback('pre_linebreak_filter', function(head) - get_ruby_side_kern(head) + if texcount["luakorubyattrcnt"]>0 then get_ruby_side_kern(head) end cjk_spacing_linebreak(head) - spread_ruby_base_box(head) + if texcount["luakorubyattrcnt"]>0 then spread_ruby_base_box(head) end head = compress_fullwidth_punctuations(head) discourage_char_widow(head, nodeslide(head)) - head = no_ruby_at_margin(head) + if texcount["luakorubyattrcnt"]>0 then head = no_ruby_at_margin(head) end head = reorderTM(head) return head end, 'luatexko.pre_linebreak_filter') @@ -1540,7 +1596,7 @@ local function after_linebreak_dotemph (head) if cc and (cc == 0 or cc == 7 or cc == 8) then local basewd = curr.width or 0 if cc == 8 then -- check next char for old hangul jung/jongseong - local nn = curr.next + local nn = nodenext(curr) while nn do if nn.id ~= glyphnode then break end local uni = get_unicode_char(nn) @@ -1550,7 +1606,7 @@ local function after_linebreak_dotemph (head) else break end - nn = nn.next + nn = nodenext(nn) end end local d = copy_node(dotemphnode[attr]) @@ -1623,7 +1679,7 @@ 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 + if ulstart and ulstart.id == gluenode then ulstart = nodenext(ulstart) end for curr in traverse(head) do if curr.id == hlistnode then curr.head,ulinenum = after_linebreak_underline(curr.head,curr.glue_order,curr.glue_set,curr.glue_sign,ulinenum) @@ -1652,48 +1708,61 @@ end -- add to callback : post-linebreak ----------------------------------- add_to_callback('vpack_filter', function(head) - head = after_linebreak_dotemph(head) - after_linebreak_ruby(head) - head = after_linebreak_underline(head) + if texcount["luakodotemphcnt"]>0 then head = after_linebreak_dotemph(head) end + if texcount["luakorubyattrcnt"]>0 then after_linebreak_ruby(head) end + if texcount["luakoulineboxcnt"]>0 then head = after_linebreak_underline(head) end return head end, 'luatexko.vpack_filter') add_to_callback("post_linebreak_filter", function(head) - head = after_linebreak_dotemph(head) - after_linebreak_ruby(head) - head = after_linebreak_underline(head) + if texcount["luakodotemphcnt"]>0 then head = after_linebreak_dotemph(head) end + if texcount["luakorubyattrcnt"]>0 then after_linebreak_ruby(head) end + if texcount["luakoulineboxcnt"]>0 then head = after_linebreak_underline(head) end return head end, 'luatexko.post_linebreak_filter') ------------------------------------ --- vertical typesetting: EXPERIMENTAL -- don't use this +-- vertical typesetting: EXPERIMENTAL ------------------------------------ ----[[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] +local tsbtable, mytime, currtime, cachedir, lfsattributes, lfstouch + +local function get_vwidth_tsb_table (filename,fontname) + if tsbtable[fontname] then return tsbtable[fontname] end + local cachefile = stringformat("%s/luatexko_vertical_metrics_%s.lua", + cachedir,stringgsub(fontname,"%W","_")) + local cattr = lfs.isfile(cachefile) and lfsattributes(cachefile) + local fonttime = lfsattributes(filename,"modification") + if cattr and cattr.access > mytime and cattr.modification == fonttime then + tsbtable[fontname] = dofile(cachefile) + return tsbtable[fontname] 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 + local metrics = nil + local font = fontloader.open(filename,fontname) + if font then + metrics = fontloader.to_table(font) + fontloader.close(font) + local glyph_t = {} + if metrics.subfonts then + for _,v in ipairs(metrics.subfonts) do + for ii,vv in pairs(v.glyphs) do + glyph_t[ii] = { ht = vv.vwidth, tsb = vv.tsidebearing } + end + end + else + for i,v in ipairs(metrics.glyphs) do + glyph_t[i] = { ht = v.vwidth, tsb = v.tsidebearing } + end end + if lfstouch then + table.tofile(cachefile,glyph_t,"return") + if not lfstouch(cachefile,currtime,fonttime) then + warn("Writing cache file '%s' failed!",cachefile) + end + end + tsbtable[fontname] = glyph_t + return glyph_t end - file:close() - tsbtable[filename] = tsbtb - return tsbtb end local function cjk_vertical_font (vf) @@ -1702,25 +1771,13 @@ local function cjk_vertical_font (vf) 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 - --]] + -- load font (again) + local tsbtable = get_vwidth_tsb_table(vf.filename,vf.fontname) + if not tsbtable then 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 = hash vf.type = 'virtual' vf.fonts = {{ id = id }} local quad = vf.parameters and vf.parameters.quad or 655360 @@ -1728,31 +1785,21 @@ local function cjk_vertical_font (vf) local ascender = vf.parameters and vf.parameters.ascender or quad*0.8 local factor = vf.parameters and vf.parameters.factor or 655.36 local xheight = vf.parameters and vf.parameters.x_height or quad/2 - local halfxht = xheight/2 + local goffset = xheight/2 - quad/2 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 - --]] + local gl = v.index + -- from loaded font + local vw = tsbtable and tsbtable[gl] and tsbtable[gl].ht vw = vw and vw * factor or quad - local hw = v.width or quad - local offset = hw/2 - quad/2 + halfxht - local vh = (hw > 0) and hw/2 or nil + local tsb = tsbtable and tsbtable[gl] and tsbtable[gl].tsb local bb4 = dsc and dsc.boundingbox and dsc.boundingbox[4] - bb4 = bb4 and bb4*factor - local asc = bb4 and tsb and (bb4 + tsb) - asc = asc or ascender + local asc = bb4 and tsb and (bb4+tsb)*factor or ascender + local hw = v.width or quad + local offset = hw/2 + goffset + local vh = hw > 0 and hw/2 or nil v.commands = { - {'right', asc}, -- bbox4 + top_side_bearing! But, tsb not available! + {'right', asc}, -- bbox4 + top_side_bearing {'down', offset}, {'special', 'pdf: q 0 1 -1 0 0 0 cm'}, {'push'}, @@ -1800,6 +1847,15 @@ local function activate_vertical_virtual (tfmdata,value) local loaded = luatexbase.priority_in_callback("luaotfload.patch_font", "luatexko.vertical_virtual_font") if value and not loaded then + require "fontloader" + require "lfs" + lfstouch = lfs.touch + lfsattributes = lfs.attributes + tsbtable = {} + currtime = os.time() + mytime = kpse_find_file("luatexko.lua") + mytime = mytime and lfsattributes(mytime,"modification") + cachedir = caches.getwritablepath("..","luatexko") add_to_callback("luaotfload.patch_font", cjk_vertical_font, "luatexko.vertical_virtual_font") @@ -1814,7 +1870,6 @@ otffeatures.register { node = activate_vertical_virtual, } } ---no vwidth in luaotfload v2]] ------------------------------------ -- italic correction for fake-slant font @@ -1834,7 +1889,62 @@ local function fakeslant_itlc (tfmdata) end end +------------------------------------ +-- tounicode for old hangul +------------------------------------ +local function tounicode_oldhangul (tfmdata) + local script = tfmdata.properties and tfmdata.properties.script + if script ~= "hang" then return end + local desc = tfmdata.shared and tfmdata.shared.rawdata and tfmdata.shared.rawdata.descriptions + local chrs = tfmdata.characters + if not desc or not chrs then return end + if not chrs[0x1100] then return end + local last = 0 + for _,v in ipairs({{0x1100,0x11FF},{0xA960,0xA97C},{0xD7B0,0xD7FB}}) do + for i = v[1],v[2] do + local ds = desc[i] and desc[i].slookups + if ds then + for _,s in pairs(ds) do + if type(s) == "number" and s >= 0xF0000 and chrs[s] and not chrs[s].tounicode then + chrs[s].tounicode = stringformat("%04X",i) + last = s > last and s or last + end + end + end + end + end + if stringfind(tfmdata.fontname,"^HCR.+LVT") then + local touni = "1112119E" + for i = last+1, last+2 do + local dsc,chr = desc[i],chrs[i] + if dsc and chr and dsc.class == "ligature" and not chr.tounicode then + chr.tounicode = touni + end + touni = touni.."11AB" + end + end +end + add_to_callback("luaotfload.patch_font", - fakeslant_itlc, - "luatexko.fakeslant_itlc") + function(tfmdata) + fakeslant_itlc(tfmdata) + tounicode_oldhangul(tfmdata) + end, "luatexko.font_patches") + +------------------------------------ +-- Actual Text +------------------------------------ +local function actualtext (str) + local t = {} + for uni in string.utfvalues(str) do + if uni < 0x10000 then + t[#t+1] = stringformat("%04X",uni) + else -- surrogate + uni = uni - 0x10000 + t[#t+1] = stringformat("%04X%04X", uni/0x400+0xD800, uni%0x400+0xDC00) + end + end + tex.sprint(stringformat("<FEFF%s>", table.concat(t))) +end +luatexko.actualtext = actualtext |