summaryrefslogtreecommitdiff
path: root/macros/luatex/latex/spacekern/spacekern.lua
diff options
context:
space:
mode:
Diffstat (limited to 'macros/luatex/latex/spacekern/spacekern.lua')
-rw-r--r--macros/luatex/latex/spacekern/spacekern.lua174
1 files changed, 174 insertions, 0 deletions
diff --git a/macros/luatex/latex/spacekern/spacekern.lua b/macros/luatex/latex/spacekern/spacekern.lua
new file mode 100644
index 0000000000..7ea754d0ab
--- /dev/null
+++ b/macros/luatex/latex/spacekern/spacekern.lua
@@ -0,0 +1,174 @@
+-- Copyright (c) 2022 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.1
+
+local ID = node.id
+local GLYPH = ID ( "glyph" )
+local GLUE = ID ( "glue" )
+local KERN = ID ( "kern" )
+local WI = ID ( "whatsit" )
+local BOUND = ID ( "boundary" )
+local PENALTY = ID ( "penalty" )
+local SWAPPED = table.swapped
+local SUBTYPES = node.subtypes
+local USERKERN = SWAPPED ( SUBTYPES ("kern") )["userkern"]
+local LBPENALTY = SWAPPED ( SUBTYPES ("penalty") )["linebreakpenalty"]
+local SPACESKIP = SWAPPED ( SUBTYPES ("glue") )["spaceskip"]
+local NEW = node.new
+local REM = node.remove
+local PREV = node.prev
+local NEXT = node.next
+local INS_B = node.insert_before
+local T_ID = node.traverse_id
+local T_GLYPH = node.traverse_glyph
+local WIS = node.whatsits()
+local userdefined
+local pairs = pairs
+local FLOOR = math.floor
+local GET_FONT = font.getfont
+local ATC = luatexbase.add_to_callback
+
+local no_iw_kern = false
+
+function spacekern_no_iw_kern ()
+ no_iw_kern = true
+end
+
+local function round ( num, dec )
+ return FLOOR ( num * 10^dec + 0.5 ) / 10^dec
+end
+
+for key, value in pairs ( WIS ) do
+ if value == "user_defined" then
+ userdefined = key
+ end
+end
+
+local function find_glyph ( n, d )
+ if d ( n ) then
+ n = d ( n )
+ while n.id ~= GLYPH do
+ if not d ( n ) or n.id == GLUE or n.id == BOUND or ( n.id == KERN and n.subtype == USERKERN ) then return false end
+ n = d ( n )
+ end
+ else
+ return false
+ end
+ return n
+end
+
+local function make_kern ( head, font, first_glyph, second_glyph, insert_point )
+ local tfmdata = GET_FONT ( font )
+ if tfmdata.resources then
+ local resources = tfmdata.resources
+ if resources.sequences then
+ local seq = resources.sequences
+ for _, t in pairs ( seq ) do
+ if t.steps then
+ local steps = t.steps
+ for _, k in pairs ( steps ) do
+ if k.coverage then
+ local first_number = true
+ if k.coverage[first_glyph] then
+ local glyph_table = k.coverage[first_glyph]
+ if type ( glyph_table ) == "table" then
+ for key, value in pairs ( glyph_table ) do
+ if key == second_glyph and type ( value ) == "number" and first_number and ( k.format == "move" or tfmdata.specification.features.raw[t.name] ) then
+ if tfmdata.specification.features.raw[t.name] then
+ first_number = false
+ end
+ if PREV ( insert_point ).id == PENALTY then
+ insert_point = PREV ( insert_point )
+ end
+ INS_B ( head, insert_point, NEW ( KERN ) )
+ PREV ( insert_point ).kern = value / tfmdata.units_per_em * tfmdata.size
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+local function check_glyph ( n, d, has_prev_next_glyph, prev_next_glyph )
+ if find_glyph ( n, d ) then
+ prev_next_glyph = find_glyph ( n, d )
+ has_prev_next_glyph = true
+ end
+ return has_prev_next_glyph, prev_next_glyph
+end
+
+local function make_kerns ( head, n )
+ local has_prev_glyph = false
+ local has_next_glyph = false
+ local prev_glyph = n
+ local next_glyph = n
+ has_prev_glyph, prev_glyph = check_glyph ( n, PREV, has_prev_glyph, prev_glyph )
+ has_next_glyph, next_glyph = check_glyph ( n, NEXT, has_next_glyph, next_glyph )
+ if has_prev_glyph and prev_glyph.char and prev_glyph.font then
+ make_kern ( head, prev_glyph.font, prev_glyph.char, 32, n )
+ end
+ if has_next_glyph and next_glyph.char and next_glyph.font then
+ make_kern ( head, next_glyph.font, 32, next_glyph.char, n )
+ end
+ if not no_iw_kern and has_prev_glyph and has_next_glyph and prev_glyph.char and next_glyph.char and prev_glyph.font and next_glyph.font and ( prev_glyph.font == next_glyph.font ) then
+ make_kern ( head, prev_glyph.font, prev_glyph.char, next_glyph.char, n )
+ end
+end
+
+local function make_glues_and_kerns ( head )
+ for n in T_ID ( GLUE, head ) do
+ make_kerns ( head, n )
+ end
+ for n in T_GLYPH ( head ) do
+ if n.char and n.char == 59 and ( not find_glyph ( n, PREV ) or find_glyph ( n, PREV ).char ~= 59 ) then
+ if find_glyph ( n, NEXT ) then
+ local next_glyph = n
+ next_glyph = find_glyph ( n, NEXT )
+ if next_glyph.char and next_glyph.char == 59 then
+ REM ( head, next_glyph )
+ local SIZE = 0
+ if n.font then
+ local tfmdata = GET_FONT ( n.font )
+ if tfmdata.size then
+ SIZE = tfmdata.size
+ end
+ end
+ INS_B ( head, n, NEW ( GLUE ) )
+ local glue_node = PREV ( n )
+ glue_node.subtype = SPACESKIP
+ glue_node.width = round ( SIZE * .16667, 0 )
+ local has_next_glyph = false
+ if find_glyph ( n, NEXT ) then
+ next_glyph = find_glyph ( n, NEXT )
+ has_next_glyph = true
+ end
+ if has_next_glyph and next_glyph.char and next_glyph.char == 59 then
+ INS_B ( head, glue_node, NEW ( PENALTY ) )
+ PREV ( glue_node ).subtype = LBPENALTY
+ PREV ( glue_node ).penalty = 10000
+ REM ( head, next_glyph )
+ end
+ REM ( head, n )
+ make_kerns ( head, glue_node )
+ end
+ end
+ end
+ end
+end
+
+ATC ( "ligaturing", make_glues_and_kerns, "kern between words and against space", 1 ) \ No newline at end of file