summaryrefslogtreecommitdiff
path: root/macros/luatex/latex/spacekern/spacekern.lua
blob: 442f3845d434f5b942f9014ec5b81439f5a76576 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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.1a

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 )