1if not modules then modules = { } end modules ['typo-itc'] = {
2 version = 1.001,
3 comment = "companion to typo-itc.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9local tonumber = tonumber
10
11local trace_italics = false trackers.register("typesetters.italics", function(v) trace_italics = v end)
12
13local report_italics = logs.reporter("nodes","italics")
14
15local threshold = 0.5 trackers.register("typesetters.threshold", function(v) threshold = v == true and 0.5 or tonumber(v) end)
16
17typesetters.italics = typesetters.italics or { }
18local italics = typesetters.italics
19
20local nodecodes = nodes.nodecodes
21local glyph_code = nodecodes.glyph
22local kern_code = nodecodes.kern
23local glue_code = nodecodes.glue
24local disc_code = nodecodes.disc
25local math_code = nodecodes.math
26
27local enableaction = nodes.tasks.enableaction
28
29local nuts = nodes.nuts
30local nodepool = nuts.pool
31
32local getprev = nuts.getprev
33local getnext = nuts.getnext
34local getid = nuts.getid
35local getchar = nuts.getchar
36local getdisc = nuts.getdisc
37local getattr = nuts.getattr
38local setattr = nuts.setattr
39local getattrlist = nuts.getattrlist
40local setattrlist = nuts.setattrlist
41local setdisc = nuts.setdisc
42local isglyph = nuts.isglyph
43local setkern = nuts.setkern
44local getkern = nuts.getkern
45local getheight = nuts.getheight
46
47local insertnodeafter = nuts.insertafter
48local remove_node = nuts.remove
49local endofmath = nuts.endofmath
50
51local texgetattribute = tex.getattribute
52local texsetattribute = tex.setattribute
53local a_italics = attributes.private("italics")
54local a_mathitalics = attributes.private("mathitalics")
55
56local unsetvalue = attributes.unsetvalue
57
58local new_correction_kern = nodepool.italickern
59local new_correction_glue = nodepool.glue
60
61local fonthashes = fonts.hashes
62local fontdata = fonthashes.identifiers
63local italicsdata = fonthashes.italics
64local exheights = fonthashes.exheights
65local chardata = fonthashes.characters
66
67local is_punctuation = characters.is_punctuation
68
69local implement = interfaces.implement
70
71local forcedvariant = false
72
73function typesetters.italics.forcevariant(variant)
74 forcedvariant = variant
75end
76
77
78
79
80local function setitalicinfont(font,char)
81 local tfmdata = fontdata[font]
82 local character = tfmdata.characters[char]
83 if character then
84 local italic = character.italic
85 if not italic then
86 local autoitalicamount = tfmdata.properties.autoitalicamount or 0
87 if autoitalicamount ~= 0 then
88 local description = tfmdata.descriptions[char]
89 if description then
90 italic = description.italic
91 if not italic then
92 local boundingbox = description.boundingbox
93 italic = boundingbox[3] - description.width + autoitalicamount
94 if italic < 0 then
95 italic = 0
96 end
97 end
98 if italic ~= 0 then
99 italic = italic * tfmdata.parameters.hfactor
100 end
101 end
102 end
103 if trace_italics then
104 report_italics("setting italic correction of %C of font %a to %p",char,font,italic)
105 end
106 if not italic then
107 italic = 0
108 end
109 character.italic = italic
110 end
111 return italic
112 else
113 return 0
114 end
115end
116
117
118
119local function okay(data,current,font,prevchar,previtalic,char,what)
120 if data then
121 if trace_italics then
122 report_italics("ignoring %p between %s italic %C and italic %C",previtalic,what,prevchar,char)
123 end
124 return false
125 end
126 if threshold then
127
128 while current and getid(current) ~= glyph_code do
129 current = getprev(current)
130 end
131 if current then
132 local ht = getheight(current)
133 local ex = exheights[font]
134 local th = threshold * ex
135 if ht <= th then
136 if trace_italics then
137 report_italics("ignoring correction between %s italic %C and regular %C, height %p less than threshold %p",prevchar,what,char,ht,th)
138 end
139 return false
140 end
141 else
142
143 end
144 end
145 if trace_italics then
146 report_italics("inserting %p between %s italic %C and regular %C",previtalic,what,prevchar,char)
147 end
148 return true
149end
150
151
152
153
154
155
156
157local function correction_kern(kern,n)
158 local k = new_correction_kern(kern)
159 if n then
160 local a = getattrlist(n)
161 if a then
162 setattrlist(k,a)
163 end
164 end
165 return k
166end
167
168local function correction_glue(glue,n)
169 local g = new_correction_glue(glue)
170 if n then
171 local a = getattrlist(n)
172 if a then
173 setattrlist(g,a)
174 end
175 end
176 return g
177end
178
179local mathokay = false
180local textokay = false
181local enablemath = false
182local enabletext = false
183
184local function domath(head,current)
185 current = endofmath(current)
186 local next = getnext(current)
187 if next then
188 local char, id = isglyph(next)
189 if char then
190
191
192 local kern = getprev(current)
193 if kern and getid(kern) == kern_code then
194 local glyph = getprev(kern)
195 if glyph and getid(glyph) == glyph_code then
196
197
198 if is_punctuation[char] then
199 local a = getattr(glyph,a_mathitalics)
200 if a and (a < 100 or a > 100) then
201 if a > 100 then
202 a = a - 100
203 else
204 a = a + 100
205 end
206 local i = getkern(kern)
207 local c, f = isglyph(glyph)
208 if getheight(next) < 1.25*exheights[f] then
209 if i == 0 then
210 if trace_italics then
211 report_italics("%s italic %p between math %C and punctuation %C","ignoring",i,c,char)
212 end
213 else
214 if trace_italics then
215 report_italics("%s italic between math %C and punctuation %C","removing",i,c,char)
216 end
217 setkern(kern,0)
218 end
219 elseif i == 0 then
220 local d = chardata[f][c]
221 local i = d.italic
222 if i == 0 then
223 if trace_italics then
224 report_italics("%s italic %p between math %C and punctuation %C","ignoring",i,c,char)
225 end
226 else
227 setkern(kern,i)
228 if trace_italics then
229 report_italics("%s italic %p between math %C and punctuation %C","setting",i,c,char)
230 end
231 end
232 elseif trace_italics then
233 report_italics("%s italic %p between math %C and punctuation %C","keeping",k,c,char)
234 end
235 end
236 end
237 end
238 else
239 local glyph = kern
240 if glyph and getid(glyph) == glyph_code then
241
242
243 if not is_punctuation[char] then
244 local a = getattr(glyph,a_mathitalics)
245 if a and (a < 100 or a > 100) then
246 if a > 100 then
247 a = a - 100
248 else
249 a = a + 100
250 end
251 if trace_italics then
252 report_italics("%s italic %p between math %C and non punctuation %C","adding",a,getchar(glyph),char)
253 end
254 insertnodeafter(head,glyph,correction_kern(a,glyph))
255 end
256 end
257 end
258 end
259 end
260 end
261 return current
262end
263
264local function mathhandler(head)
265 local current = head
266 while current do
267 if getid(current) == math_code then
268 current = domath(head,current)
269 end
270 current = getnext(current)
271 end
272 return head
273end
274
275local function texthandler(head)
276
277 local prev = nil
278 local prevchar = nil
279 local prevhead = head
280 local previtalic = 0
281 local previnserted = nil
282
283 local pre = nil
284 local pretail = nil
285
286 local post = nil
287 local posttail = nil
288 local postchar = nil
289 local posthead = nil
290 local postitalic = 0
291 local postinserted = nil
292
293 local replace = nil
294 local replacetail = nil
295 local replacechar = nil
296 local replacehead = nil
297 local replaceitalic = 0
298 local replaceinserted = nil
299
300 local current = prevhead
301 local lastfont = nil
302 local lastattr = nil
303
304 while current do
305 local char, id = isglyph(current)
306 if char then
307 local font = id
308 local data = italicsdata[font]
309 if font ~= lastfont then
310 if previtalic ~= 0 then
311 if okay(data,current,font,prevchar,previtalic,char,"glyph") then
312 insertnodeafter(prevhead,prev,correction_kern(previtalic,current))
313 end
314 elseif previnserted and data then
315 if trace_italics then
316 report_italics("deleting last correction before %s %C",char,"glyph")
317 end
318 remove_node(prevhead,previnserted,true)
319 else
320
321 if replaceitalic ~= 0 then
322 if okay(data,replace,font,replacechar,replaceitalic,char,"replace") then
323 insertnodeafter(replacehead,replace,correction_kern(replaceitalic,current))
324 end
325 replaceitalic = 0
326 elseif replaceinserted and data then
327 if trace_italics then
328 report_italics("deleting last correction before %s %C","replace",char)
329 end
330 remove_node(replacehead,replaceinserted,true)
331 end
332
333 if postitalic ~= 0 then
334 if okay(data,post,font,postchar,postitalic,char,"post") then
335 insertnodeafter(posthead,post,correction_kern(postitalic,current))
336 end
337 postitalic = 0
338 elseif postinserted and data then
339 if trace_italics then
340 report_italics("deleting last correction before %s %C","post",char)
341 end
342 remove_node(posthead,postinserted,true)
343 end
344 end
345
346 lastfont = font
347 end
348 if data then
349 local attr = forcedvariant or getattr(current,a_italics)
350 if attr and attr > 0 then
351 local cd = data[char]
352 if not cd then
353
354 previtalic = 0
355 else
356 previtalic = cd.italic
357 if not previtalic then
358 previtalic = setitalicinfont(font,char)
359
360 end
361 if previtalic ~= 0 then
362 lastfont = font
363 lastattr = attr
364 prev = current
365
366 prevchar = char
367 end
368 end
369 else
370 previtalic = 0
371 end
372 else
373 previtalic = 0
374 end
375 previnserted = nil
376 replaceinserted = nil
377 postinserted = nil
378 elseif id == disc_code then
379 previnserted = nil
380 previtalic = 0
381 replaceinserted = nil
382 replaceitalic = 0
383 postinserted = nil
384 postitalic = 0
385 updated = false
386 replacefont = nil
387 postfont = nil
388 pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
389 if replace then
390 local current = replacetail
391 while current do
392 local char, id = isglyph(current)
393 if char then
394 local font = id
395 if font ~= lastfont then
396 local data = italicsdata[font]
397 if data then
398 local attr = forcedvariant or getattr(current,a_italics)
399 if attr and attr > 0 then
400 local cd = data[char]
401 if not cd then
402
403 replaceitalic = 0
404 else
405 replaceitalic = cd.italic
406 if not replaceitalic then
407 replaceitalic = setitalicinfont(font,char)
408
409 end
410 if replaceitalic ~= 0 then
411 lastfont = font
412 lastattr = attr
413 replacechar = char
414 replacehead = replace
415 updated = true
416 end
417 end
418 end
419 end
420 replacefont = font
421 end
422 break
423 else
424 current = getprev(current)
425 end
426 end
427 end
428 if post then
429 local current = posttail
430 while current do
431 local char, id = isglyph(current)
432 if char then
433 local font = id
434 if font ~= lastfont then
435 local data = italicsdata[font]
436 if data then
437 local attr = forcedvariant or getattr(current,a_italics)
438 if attr and attr > 0 then
439 local cd = data[char]
440 if not cd then
441
442
443 else
444 postitalic = cd.italic
445 if not postitalic then
446 postitalic = setitalicinfont(font,char)
447
448 end
449 if postitalic ~= 0 then
450 lastfont = font
451 lastattr = attr
452 postchar = char
453 posthead = post
454 updated = true
455 end
456 end
457 end
458 end
459 postfont = font
460 end
461 break
462 else
463 current = getprev(current)
464 end
465 end
466 end
467 if replacefont or postfont then
468 lastfont = replacefont or postfont
469 end
470 if updated then
471 setdisc(current,pre,post,replace)
472 end
473 elseif id == kern_code then
474 previnserted = nil
475 previtalic = 0
476 replaceinserted = nil
477 replaceitalic = 0
478 postinserted = nil
479 postitalic = 0
480 elseif id == glue_code then
481 if previtalic ~= 0 then
482 if trace_italics then
483 report_italics("inserting %p between %s italic %C and glue",previtalic,"glyph",prevchar)
484 end
485 previnserted = correction_glue(previtalic,current)
486 previtalic = 0
487 insertnodeafter(prevhead,prev,previnserted)
488 else
489 if replaceitalic ~= 0 then
490 if trace_italics then
491 report_italics("inserting %p between %s italic %C and glue",replaceitalic,"replace",replacechar)
492 end
493 replaceinserted = correction_kern(replaceitalic,current)
494 replaceitalic = 0
495 insertnodeafter(replacehead,replace,replaceinserted)
496 end
497 if postitalic ~= 0 then
498 if trace_italics then
499 report_italics("inserting %p between %s italic %C and glue",postitalic,"post",postchar)
500 end
501 postinserted = correction_kern(postitalic,current)
502 postitalic = 0
503 insertnodeafter(posthead,post,postinserted)
504 end
505 end
506 elseif id == math_code then
507
508 previnserted = nil
509 previtalic = 0
510 replaceinserted = nil
511 replaceitalic = 0
512 postinserted = nil
513 postitalic = 0
514 if mathokay then
515 current = domath(head,current)
516 else
517 current = endofmath(current)
518 end
519 else
520 if previtalic ~= 0 then
521 if trace_italics then
522 report_italics("inserting %p between %s italic %C and whatever",previtalic,"glyph",prevchar)
523 end
524 insertnodeafter(prevhead,prev,correction_kern(previtalic,current))
525 previnserted = nil
526 previtalic = 0
527 replaceinserted = nil
528 replaceitalic = 0
529 postinserted = nil
530 postitalic = 0
531 else
532 if replaceitalic ~= 0 then
533 if trace_italics then
534 report_italics("inserting %p between %s italic %C and whatever",replaceitalic,"replace",replacechar)
535 end
536 insertnodeafter(replacehead,replace,correction_kern(replaceitalic,current))
537 previnserted = nil
538 previtalic = 0
539 replaceinserted = nil
540 replaceitalic = 0
541 postinserted = nil
542 postitalic = 0
543 end
544 if postitalic ~= 0 then
545 if trace_italics then
546 report_italics("inserting %p between %s italic %C and whatever",postitalic,"post",postchar)
547 end
548 insertnodeafter(posthead,post,correction_kern(postitalic,current))
549 previnserted = nil
550 previtalic = 0
551 replaceinserted = nil
552 replaceitalic = 0
553 postinserted = nil
554 postitalic = 0
555 end
556 end
557 end
558 current = getnext(current)
559 end
560 if lastattr and lastattr > 1 then
561 if previtalic ~= 0 then
562 if trace_italics then
563 report_italics("inserting %p between %s italic %C and end of list",previtalic,"glyph",prevchar)
564 end
565 insertnodeafter(prevhead,prev,correction_kern(previtalic,current))
566 else
567 if replaceitalic ~= 0 then
568 if trace_italics then
569 report_italics("inserting %p between %s italic %C and end of list",replaceitalic,"replace",replacechar)
570 end
571 insertnodeafter(replacehead,replace,correction_kern(replaceitalic,current))
572 end
573 if postitalic ~= 0 then
574 if trace_italics then
575 report_italics("inserting %p between %s italic %C and end of list",postitalic,"post",postchar)
576 end
577 insertnodeafter(posthead,post,correction_kern(postitalic,current))
578 end
579 end
580 end
581 return head
582end
583
584function italics.handler(head)
585 if textokay then
586 return texthandler(head)
587 elseif mathokay then
588 return mathhandler(head)
589 else
590 return head, false
591 end
592end
593
594enabletext = function()
595 enableaction("processors","typesetters.italics.handler")
596 if trace_italics then
597 report_italics("enabling text/text italics")
598 end
599 enabletext = false
600 textokay = true
601end
602
603enablemath = function()
604 enableaction("processors","typesetters.italics.handler")
605 if trace_italics then
606 report_italics("enabling math/text italics")
607 end
608 enablemath = false
609 mathokay = true
610end
611
612function italics.enabletext()
613 if enabletext then
614 enabletext()
615 end
616end
617
618function italics.enablemath()
619 if enablemath then
620 enablemath()
621 end
622end
623
624function italics.set(n)
625 if enabletext then
626 enabletext()
627 end
628 if n == variables.reset then
629 texsetattribute(a_italics,unsetvalue)
630 else
631 texsetattribute(a_italics,tonumber(n) or unsetvalue)
632 end
633end
634
635function italics.reset()
636 texsetattribute(a_italics,unsetvalue)
637end
638
639implement {
640 name = "setitaliccorrection",
641 actions = italics.set,
642 arguments = "string"
643}
644
645implement {
646 name = "resetitaliccorrection",
647 actions = italics.reset,
648}
649
650local variables = interfaces.variables
651local settings_to_hash = utilities.parsers.settings_to_hash
652
653local function setupitaliccorrection(option)
654 if enabletext then
655 enabletext()
656 end
657 local options = settings_to_hash(option)
658 local variant = unsetvalue
659 if options[variables.text] then
660 variant = 1
661 elseif options[variables.always] then
662 variant = 2
663 end
664
665 if options[variables.global] then
666 forcedvariant = variant
667 texsetattribute(a_italics,unsetvalue)
668 else
669 forcedvariant = false
670 texsetattribute(a_italics,variant)
671 end
672 if trace_italics then
673 report_italics("forcing %a, variant %a",forcedvariant or "-",variant ~= unsetvalue and variant)
674 end
675end
676
677implement {
678 name = "setupitaliccorrection",
679 actions = setupitaliccorrection,
680 arguments = "string"
681}
682
683
684
685local stack = { }
686
687implement {
688 name = "pushitaliccorrection",
689 actions = function()
690 table.insert(stack,{forcedvariant, texgetattribute(a_italics) })
691 end
692}
693
694implement {
695 name = "popitaliccorrection",
696 actions = function()
697 local top = table.remove(stack)
698 forcedvariant = top[1]
699 texsetattribute(a_italics,top[2])
700 end
701}
702 |