-- Copyright (c) 2022-2023 Thomas Kelkel kelkel@emaileon.de -- This file 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 2009/09/24 or later. -- Version: 0.3 local ID = node.id local GLYPH = ID ( "glyph" ) local DISC = ID ( "disc" ) local GLUE = ID ( "glue" ) local KERN = ID ( "kern" ) local WI = ID ( "whatsit" ) local HLIST = ID ( "hlist" ) local VLIST = ID ( "vlist" ) local INS = ID ( "ins" ) local SWAPPED = table.swapped local SUBTYPES = node.subtypes local WIS = node.whatsits local PDF_LITERAL = SWAPPED ( WIS () )["pdf_literal"] local LEADERS = SWAPPED ( SUBTYPES ("glue") )["leaders"] local RIGHTSKIP = SWAPPED ( SUBTYPES ("glue") )["rightskip"] local NEW = node.new local COPY = node.copy local REM = node.remove local PREV = node.prev local NEXT = node.next local INS_B = node.insert_before local INS_A = node.insert_after local HAS_GLYPH = node.has_glyph local T = node.traverse local T_ID = node.traverse_id local T_GLYPH = node.traverse_glyph local FLOOR = math.floor local GET_FONT = font.getfont local ATC = luatexbase.add_to_callback ----- local make = false local on_top = false local DIR = PREV local AB = INS_B local f = 1 local color = "1 .25 .45" function make_not_on_top () on_top = false DIR = PREV AB = INS_B f = 1 end function make_on_top () on_top = true DIR = NEXT AB = INS_A f = - 1 end local in_use = make_not_on_top function showhyphenation_make () make = true end function showhyphenation_on_top () in_use = make_on_top in_use() end function showhyphenation_lime () color = ".75 1 .25" end local function round ( num, dec ) return FLOOR ( num * 10^dec + 0.5 ) / 10^dec end local function calc_value ( value ) value = round ( value / 65781, 3 ) return value end local function get_x_value ( n, x_value, minus, half ) local exp_fac = 1 local f = 1 if minus then f = - 1 end local ff = 1 if half then ff = 0.5 end if n.expansion_factor then exp_fac = n.expansion_factor / 1000000 + 1 end if n.id == GLYPH then x_value = x_value + calc_value ( n.width ) * exp_fac * f elseif n.id == KERN then x_value = x_value + calc_value ( n.kern ) * exp_fac * f * ff end return x_value end local line_end_count = 0 local function find_glyph ( n, d, kern_value, linebreak, lig ) local line_end = nil local ligtype_mark = nil local kern_at_disc = 0 if d ( n ) then n = d ( n ) while not ( n.id == GLYPH and n.char ~= 45 or n.replace and HAS_GLYPH ( n.replace ) ) do if n.id == GLUE or n.subtype == LEADERS or n.id == INS then return false end if n.width and not ( n.char and n.char == 45 ) then kern_value = get_x_value ( n, kern_value ) -- rules, etc. elseif n.kern then local factor = 1 if linebreak or not lig then factor = 0.5 -- hyphen kern kern_at_disc = get_x_value ( n, kern_at_disc ) * factor end kern_value = get_x_value ( n, kern_value ) * factor elseif n.replace then for kern_node in T_ID ( KERN, n.replace ) do kern_value = get_x_value ( kern_node, kern_value ) end end if n.char == 45 then line_end = true line_end_count = line_end_count + 1 end if line_end and n.user_id == 848485 then ligtype_mark = true end if d ( n ) then n = d ( n ) else return false end end if n.replace then local REPLACE = n.replace for some_node in T ( REPLACE ) do if some_node.width then kern_value = get_x_value ( some_node, kern_value ) elseif some_node.kern then kern_value = get_x_value ( some_node, kern_value ) end end else if n.width then kern_value = get_x_value ( n, kern_value ) end end end return n, kern_value, ligtype_mark, kern_at_disc end local function check_linebreak ( n, d ) while n.id ~= GLYPH do if d ( n ) then n = d ( n ) else break end end if n.char == 45 and d == PREV or n.id == GLUE and n.subtype == RIGHTSKIP and d == NEXT then make_not_on_top() return true end end local function hyphenation_points ( head ) for n in T ( head ) do if n.id == HLIST or n.id == VLIST then n.head = hyphenation_points ( n.head ) elseif n.id == DISC then check_linebreak ( n, PREV ) check_linebreak ( n, NEXT ) local lig_add = 0 local prev_next_kern = 0 local prev_next_kern_lig = 0 local pre_kerns = 0 local second_pre_glyph = 0 local lig = false if n.replace then if not HAS_GLYPH ( n.replace ) then local REPLACE = n.replace for some_node in T ( REPLACE ) do if some_node.kern and not check_linebreak ( n, DIR ) then prev_next_kern_lig = get_x_value ( some_node, prev_next_kern_lig ) * 0.5 end end else local gn_counter = 0 for glyph_node in T_GLYPH ( n.replace ) do if glyph_node.components then lig = true end end if n.post then for _ in T_ID ( KERN, n.pre ) do pre_kerns = pre_kerns + 1 end for glyph_node in T_GLYPH ( n.pre ) do if glyph_node.width then lig_add = get_x_value ( glyph_node, lig_add ) break -- hyphen! end end for glyph_node in T_GLYPH ( n.replace ) do if glyph_node.width then gn_counter = gn_counter + 1 if gn_counter == 2 then second_pre_glyph = get_x_value ( glyph_node, second_pre_glyph, true ) end end end if not on_top then if n.replace.kern then prev_next_kern_lig = get_x_value ( n.replace, prev_next_kern_lig ) end end else if n.replace.kern then prev_next_kern_lig = get_x_value ( n.replace, prev_next_kern_lig ) * 0.5 end for glyph_node in T_GLYPH ( n.replace ) do -- short-armed f if glyph_node.char ~= 45 then lig_add = get_x_value ( glyph_node, lig_add ) end end end if on_top then for some_node in T ( n.replace ) do if some_node.width then lig_add = get_x_value ( some_node, lig_add, true ) elseif some_node.kern and some_node ~= n.replace then lig_add = get_x_value ( some_node, lig_add, true ) end end end if gn_counter > 1 then local counter = 0 if not on_top then for kern_node in T_ID ( KERN, n.replace ) do counter = counter + 1 if counter > 1 then if pre_kerns > 1 then if pre_kerns > 2 then prev_next_kern_lig = get_x_value ( kern_node, prev_next_kern_lig ) + lig_add else prev_next_kern_lig = get_x_value ( kern_node, prev_next_kern_lig, false, true ) end pre_kerns = pre_kerns - 1 end end end else for kern_node in T_ID ( KERN, n.replace ) do counter = counter + 1 if counter > 1 then if pre_kerns > 1 then if pre_kerns > 2 then prev_next_kern_lig = get_x_value ( kern_node, prev_next_kern_lig, true ) + second_pre_glyph else prev_next_kern_lig = get_x_value ( kern_node, prev_next_kern_lig, true, true ) end pre_kerns = pre_kerns - 1 end end end end end end end local prev_next_glyph = n local ligtype_mark = nil if find_glyph ( prev_next_glyph, DIR, 0 ) then prev_next_glyph, prev_next_kern, ligtype_mark = find_glyph ( prev_next_glyph, DIR, 0, check_linebreak ( n, PREV ) or check_linebreak ( n, NEXT ), lig ) end local reverse = PREV if DIR == PREV then reverse = NEXT end if find_glyph ( n, reverse, 0 ) then local _, __, ___, kern_at_disc = find_glyph ( n, reverse, 0, check_linebreak ( n, PREV ) or check_linebreak ( n, NEXT ), lig ) if kern_at_disc then prev_next_kern = prev_next_kern + kern_at_disc end end head = AB ( head, prev_next_glyph, NEW ( WI, PDF_LITERAL ) ) lig_add = lig_add + ( prev_next_kern + prev_next_kern_lig ) * f DIR ( prev_next_glyph ).mode = 0 local size_factor = 1 if font.current() then size_factor = calc_value ( GET_FONT ( font.current() ).size / 10 ) end local DATA if PREV ( n ) and PREV ( n ).user_id and PREV ( n ).user_id == 848485 or ligtype_mark then DATA = "q " .. lig_add - 2 * size_factor .. " " .. -3 * size_factor .. " m " .. lig_add + 2 * size_factor .. " " .. -3 * size_factor .. " l " .. lig_add + 2 * size_factor .. " " .. -4 * size_factor .. " l " .. lig_add - 2 * size_factor .. " " .. -4 * size_factor .. " l " .. color .. " rg f Q" else DATA = "q " .. lig_add .. " 0 m " .. lig_add + 2 * size_factor .. " " .. -3 * size_factor .. " l " .. lig_add - 2 * size_factor .. " " .. -3 * size_factor .. " l " .. color .. " rg f Q" end DIR ( prev_next_glyph ).data = DATA in_use() end end return head end local function make_hyphens ( head ) for n in T_ID ( DISC, head ) do local REPLACE = n.replace for some_node in T ( REPLACE ) do REM ( REPLACE, some_node ) end local PRE = n.pre for some_node in T_GLYPH ( PRE ) do REPLACE = INS_A ( REPLACE, REPLACE, COPY ( some_node ) ) end local POST = n.post for some_node in T_GLYPH ( POST ) do REPLACE = INS_A ( REPLACE, REPLACE, COPY ( some_node ) ) end n.replace = REPLACE end end function showhyphenation_start () if make then ATC ( "ligaturing", make_hyphens, "make hyphens" ) else ATC ( "post_linebreak_filter", hyphenation_points, "show hyphenation points in postline", 1 ) ATC ( "hpack_filter", hyphenation_points, "show hyphenation points in hpack" ) end end