1if not modules then modules = { } end modules ['spac-chr'] = {
2 version = 1.001,
3 optimize = true,
4 comment = "companion to spac-chr.mkiv",
5 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6 copyright = "PRAGMA ADE / ConTeXt Development Team",
7 license = "see context related readme files"
8}
9
10local byte, lower = string.byte, string.lower
11
12
13
14
15
16
17
18
19
20local next = next
21
22local trace_characters = false trackers.register("typesetters.characters", function(v) trace_characters = v end)
23local trace_nbsp = false trackers.register("typesetters.nbsp", function(v) trace_nbsp = v end)
24
25local report_characters = logs.reporter("typesetting","characters")
26
27local nodes, node = nodes, node
28
29local nuts = nodes.nuts
30
31local getboth = nuts.getboth
32local getnext = nuts.getnext
33local getprev = nuts.getprev
34local getattr = nuts.getattr
35local setattr = nuts.setattr
36local getlanguage = nuts.getlanguage
37local setchar = nuts.setchar
38local setattrlist = nuts.setattrlist
39local getfont = nuts.getfont
40local setsubtype = nuts.setsubtype
41local isglyph = nuts.isglyph
42
43local setcolor = nodes.tracers.colors.set
44
45local insertnodebefore = nuts.insertbefore
46local insertnodeafter = nuts.insertafter
47local remove_node = nuts.remove
48local nextchar = nuts.traversers.char
49local nextglyph = nuts.traversers.glyph
50
51local copy_node = nuts.copy
52
53local nodepool = nuts.pool
54local new_penalty = nodepool.penalty
55local new_glue = nodepool.glue
56local new_kern = nodepool.kern
57local new_rule = nodepool.rule
58
59local nodecodes = nodes.nodecodes
60local gluecodes = nodes.gluecodes
61
62local glyph_code = nodecodes.glyph
63local spaceskip_code = gluecodes.spaceskip
64
65local chardata = characters.data
66local is_punctuation = characters.is_punctuation
67
68local typesetters = typesetters
69
70local unicodeblocks = characters.blocks
71
72local characters = typesetters.characters or { }
73typesetters.characters = characters
74
75local fonthashes = fonts.hashes
76local fontparameters = fonthashes.parameters
77local fontcharacters = fonthashes.characters
78local fontquads = fonthashes.quads
79
80local setmetatableindex = table.setmetatableindex
81
82local a_character = attributes.private("characters")
83local a_alignstate = attributes.private("alignstate")
84
85local c_zero = byte('0')
86local c_period = byte('.')
87
88local function inject_quad_space(unicode,head,current,fraction)
89 if fraction ~= 0 then
90 fraction = fraction * fontquads[getfont(current)]
91 end
92 local glue = new_glue(fraction)
93 setattrlist(glue,current)
94 setattrlist(current)
95 setattr(glue,a_character,unicode)
96 return insertnodeafter(head,current,glue)
97end
98
99local function inject_char_space(unicode,head,current,parent)
100 local font = getfont(current)
101 local char = fontcharacters[font][parent]
102 local glue = new_glue(char and char.width or fontparameters[font].space)
103 setattrlist(glue,current)
104 setattrlist(current)
105 setattr(glue,a_character,unicode)
106 return insertnodeafter(head,current,glue)
107end
108
109local function inject_nobreak_space(unicode,head,current,space,spacestretch,spaceshrink)
110 local glue = new_glue(space,spacestretch,spaceshrink)
111 local penalty = new_penalty(10000)
112 setattrlist(glue,current)
113 setattrlist(current)
114 setattr(glue,a_character,unicode)
115 head, current = insertnodeafter(head,current,penalty)
116 if trace_nbsp then
117 local rule = new_rule(space)
118 local kern = new_kern(-space)
119 local penalty = new_penalty(10000)
120 setcolor(rule,"orange")
121 head, current = insertnodeafter(head,current,rule)
122 head, current = insertnodeafter(head,current,kern)
123 head, current = insertnodeafter(head,current,penalty)
124 end
125 return insertnodeafter(head,current,glue)
126end
127
128local function nbsp(head,current)
129 local para = fontparameters[getfont(current)]
130 local attr = getattr(current,a_alignstate) or 0
131 if attr >= 1 and attr <= 3 then
132 head, current = inject_nobreak_space(0x00A0,head,current,para.space,0,0)
133 else
134 head, current = inject_nobreak_space(0x00A0,head,current,para.space,para.space_stretch,para.space_shrink)
135 end
136 setsubtype(current,spaceskip_code)
137 return head, current
138end
139
140
141
142function characters.replacenbsp(head,original)
143 local head, current = nbsp(head,original)
144 return remove_node(head,original,true)
145end
146
147function characters.replacenbspaces(head)
148 local wipe = false
149 for current, char, font in nextglyph, head do
150 if char == 0x00A0 then
151 if wipe then
152 head = remove_node(h,current,true)
153 wipe = false
154 end
155 local h = nbsp(head,current)
156 if h then
157 wipe = current
158 end
159 end
160 end
161 if wipe then
162 head = remove_node(head,current,true)
163 end
164 return head
165end
166
167
168
169
170local nbsphash = { } setmetatableindex(nbsphash,function(t,k)
171
172 for i=unicodeblocks.devanagari.first,unicodeblocks.devanagari.last do nbsphash[i] = true end
173 for i=unicodeblocks.kannada .first,unicodeblocks.kannada .last do nbsphash[i] = true end
174 setmetatableindex(nbsphash,nil)
175 return nbsphash[k]
176end)
177
178local methods = {
179
180
181
182
183
184
185 [0x001F] = function(head,current)
186 local next = getnext(current)
187 if next then
188 local char, font = isglyph(next)
189 if char then
190 head, current = remove_node(head,current,true)
191 if not is_punctuation[char] then
192 local p = fontparameters[font]
193 head, current = insertnodebefore(head,current,new_glue(p.space,p.space_stretch,p.space_shrink))
194 end
195 end
196 end
197 end,
198
199 [0x00A0] = function(head,current)
200 local prev, next = getboth(current)
201 if next then
202 local char = isglyph(current)
203 if not char then
204
205 elseif char == 0x200C or char == 0x200D then
206 next = getnext(next)
207 if next then
208 char = isglyph(next)
209 if char and nbsphash[char] then
210 return false
211 end
212 end
213 elseif nbsphash[char] then
214 return false
215 end
216 end
217 if prev then
218 local char = isglyph(prev)
219 if char and nbsphash[char] then
220 return false
221 end
222 end
223 return nbsp(head,current)
224 end,
225
226 [0x00AD] = function(head,current)
227 return insertnodeafter(head,current,languages.explicithyphen(current))
228 end,
229
230 [0x2000] = function(head,current)
231 return inject_quad_space(0x2000,head,current,1/2)
232 end,
233
234 [0x2001] = function(head,current)
235 return inject_quad_space(0x2001,head,current,1)
236 end,
237
238 [0x2002] = function(head,current)
239 return inject_quad_space(0x2002,head,current,1/2)
240 end,
241
242 [0x2003] = function(head,current)
243 return inject_quad_space(0x2003,head,current,1)
244 end,
245
246 [0x2004] = function(head,current)
247 return inject_quad_space(0x2004,head,current,1/3)
248 end,
249
250 [0x2005] = function(head,current)
251 return inject_quad_space(0x2005,head,current,1/4)
252 end,
253
254 [0x2006] = function(head,current)
255 return inject_quad_space(0x2006,head,current,1/6)
256 end,
257
258 [0x2007] = function(head,current)
259 return inject_char_space(0x2007,head,current,c_zero)
260 end,
261
262 [0x2008] = function(head,current)
263 return inject_char_space(0x2008,head,current,c_period)
264 end,
265
266 [0x2009] = function(head,current)
267 return inject_quad_space(0x2009,head,current,1/8)
268 end,
269
270 [0x200A] = function(head,current)
271 return inject_quad_space(0x200A,head,current,1/8)
272 end,
273
274 [0x200B] = function(head,current)
275 return inject_quad_space(0x200B,head,current,0)
276 end,
277
278 [0x202F] = function(head,current)
279 return inject_nobreak_space(0x202F,head,current,fontquads[getfont(current)]/8)
280 end,
281
282 [0x205F] = function(head,current)
283 return inject_nobreak_space(0x205F,head,current,4*fontquads[getfont(current)]/18)
284 end,
285
286
287
288
289
290
291
292}
293
294characters.methods = methods
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324function characters.handler(head)
325 local wipe = false
326 for current, char in nextchar, head do
327 local method = methods[char]
328 if method then
329 if wipe then
330 head = remove_node(head,wipe,true)
331 wipe = false
332 end
333 if trace_characters then
334 report_characters("replacing character %C, description %a",char,lower(chardata[char].description))
335 end
336 local h = method(head,current)
337 if h then
338 wipe = current
339 end
340 end
341 end
342 if wipe then
343 head = remove_node(head,wipe,true)
344 end
345 return head
346end
347 |