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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
-- Lua code for the selnolig package.
-- To be loaded with an instruction such as
-- \directlua{ require("selnolig.lua") }
-- from a (Lua)LaTeX .sty file.
--
-- Author: Mico Loretan (loretan dot mico at gmail dot com)
-- (with crucial contributions from Taco Hoekwater,
-- Patrick Gundlach, and Steffen Hildebrandt)
--
-- The entire selnolig package is placed under the terms
-- of the LaTeX Project Public License, version 1.3 or
-- later. (http://www.latex-project.org/lppl.txt).
-- It has the status "maintained".
selnolig = { }
selnolig.module = {
name = "selnolig",
version = "0.256",
date = "2015/10/26",
description = "Selective suppression of typographic ligatures",
author = "Mico Loretan",
copyright = "Mico Loretan",
license = "LPPL 1.3 or later"
}
debug=false -- default: don't output detailed information
-- Define variables corresponding to various text nodes;
-- cf. sections 8.1.2 and 8.1.4 of LuaTeX reference guide
local rule = node.id('rule')
local glue = node.id("glue") --
local kern = node.id('kern')
local glyph = node.id('glyph') --
local whatsit = node.id("whatsit") --
local userdefined
for n,v in pairs ( node.whatsits() ) do
if v == 'user_defined' then userdefined = n end
end
local identifier = 123456 -- any unique identifier
local noliga={}
local keepliga={} -- String -> Boolean
function debug_info(s)
if debug then
texio.write_nl(s)
end
end
local blocknode = node.new(whatsit, userdefined)
blocknode.type = 100
blocknode.user_id = identifier
local suppression_on = true -- if false, process_ligatures won't do anything
local prefix_length = function(word, byte)
return unicode.utf8.len( string.sub(word,0,byte) )
end
-- Problem: string.find and unicode.utf8.find return
-- the byte-position at which the pattern is found
-- instead of the character-position. Fix this by
-- providing a dedicated string search function.
local unicode_find = function(s, pattern, position)
-- Start by correcting the incoming position
if position ~= nil then
-- debug_info("Position: "..position)
sub = string.sub(s, 1, position)
position=position+string.len(sub) - unicode.utf8.len(sub)
-- debug_info("Corrected position: "..position)
end
-- Now execute find and fix it accordingly
byte_pos = unicode.utf8.find(s, pattern, position)
if byte_pos ~= nil then
-- "convert" byte_pos to "unicode_pos"
return unicode.utf8.len( string.sub(s, 1, byte_pos) )
else
return nil
end
end
function process_ligatures(nodes,tail)
if not suppression_on then
return -- suppression disabled
end
local s={}
local current_node=nodes
local build_liga_table = function(strlen,t)
local p={}
for i = 1, strlen do
p[i]=0
end
for k,v in pairs(t) do
-- debug_info("Match: "..v[3])
local c= unicode_find(noliga[v[3]],"|")
local correction=1
while c~=nil do
--debug_info("Position "..(v[1]+c))
p[v[1]+c-correction] = 1
c = unicode_find(noliga[v[3]],"|",c+1)
correction = correction+1
end
end
--debug_info("Liga table: "..table.concat(p, ""))
return p
end
local apply_ligatures=function(head,ligatures)
local i=1
local hh=head
local last=node.tail(head)
for curr in node.traverse_id(glyph,head) do
if ligatures[i]==1 then
debug_info("Inserting nolig whatsit before glyph: " ..unicode.utf8.char(curr.char))
node.insert_before(hh,curr, node.copy(blocknode))
hh=curr
end
last=curr
if i==#ligatures then
-- debug_info("Leave node list on position: "..i)
break
end
i=i+1
end
if(last~=nil) then
debug_info("Last char: "..unicode.utf8.char(last.char))
end
end
for t in node.traverse(nodes) do
if t.id==glyph then
s[#s+1]=unicode.utf8.char(t.char)
end
if ( t.id==glue or t.next==nil or t.id==kern or t.id==rule ) then
local f=string.gsub(table.concat(s,""),"[\\?!,\\.]+","")
local throwliga={}
for k,v in pairs (noliga) do
local count=1
local match = string.find(f,k)
while match do
count = match
keep = false
debug_k1 = ""
for k1,v1 in pairs (keepliga) do
if v1 and string.find(f,k1) and string.find(k1,k) then
debug_k1=k1
keep=true
break
end
end
if not keep then
debug_info("pattern match: "..f .." - "..k)
local n = match + string.len(k) - 1
table.insert(throwliga,{prefix_length(f,match),n,k})
else
debug_info("pattern match nolig and keeplig: "..f .." - "..k.." - "..debug_k1)
end
match= string.find(f,k,count+1)
end
end
if #throwliga==0 then
-- debug_info("No ligature suppression for: "..f)
else
debug_info("Do ligature suppression for: "..f)
local ligabreaks = build_liga_table(f:len(),throwliga)
apply_ligatures(current_node,ligabreaks)
end
s = {}
current_node = t
end
end
end -- end of function process_ligatures(nodes,tail)
function suppress_liga(s,t)
noliga[s] = t
end
function always_keep_liga(s)
keepliga[s] = true
end
function enable_suppression(val)
suppression_on = val
if val then
debug_info("Turning ligature suppression back on")
else
debug_info("Turning ligature suppression off")
end
end
function enableselnolig()
luatexbase.add_to_callback( "ligaturing",
process_ligatures, "Suppress ligatures selectively", 1 )
end
function disableselnolig()
luatexbase.remove_from_callback( "ligaturing",
"Suppress ligatures selectively" )
end
|