aboutsummaryrefslogtreecommitdiff
path: root/arabluatex.lua
diff options
context:
space:
mode:
Diffstat (limited to 'arabluatex.lua')
-rw-r--r--arabluatex.lua228
1 files changed, 169 insertions, 59 deletions
diff --git a/arabluatex.lua b/arabluatex.lua
index 8897194..114a569 100644
--- a/arabluatex.lua
+++ b/arabluatex.lua
@@ -2,24 +2,23 @@
2This file is part of the `arabluatex' package 2This file is part of the `arabluatex' package
3 3
4ArabLuaTeX -- Processing ArabTeX notation under LuaLaTeX 4ArabLuaTeX -- Processing ArabTeX notation under LuaLaTeX
5Copyright (C) 2016--2020 Robert Alessi 5Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023
6Robert Alessi <alessi@robertalessi.net>
6 7
7Please send error reports and suggestions for improvements to Robert 8Permission to use, copy, modify, and distribute this software for any
8Alessi <alessi@robertalessi.net> 9purpose with or without fee is hereby granted, provided that the above
10copyright notice and this permission notice appear in all copies.
9 11
10This program is free software: you can redistribute it and/or modify 12THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11it under the terms of the GNU General Public License as published by 13WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12the Free Software Foundation, either version 3 of the License, or 14MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13(at your option) any later version. 15ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 19
15This program is distributed in the hope that it will be useful, but 20Please send error reports and suggestions for improvements to Robert
16WITHOUT ANY WARRANTY; without even the implied warranty of 21Alessi <alessi@robertalessi.net>
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program. If not, see
22<http://www.gnu.org/licenses/>.
23--]] 22--]]
24 23
25arabluatex = {} 24arabluatex = {}
@@ -54,11 +53,26 @@ local cmd = lpeg.Cs(dblbkslash * ascii^1 * cmdstar^-1)
54local rawcmd = lpeg.Cs(dblbkslash * ascii^1) 53local rawcmd = lpeg.Cs(dblbkslash * ascii^1)
55local aftercmd = lpeg.Cs(lpeg.S("*[{,.?;:'`\"") + dblbkslash) 54local aftercmd = lpeg.Cs(lpeg.S("*[{,.?;:'`\"") + dblbkslash)
56local cmdargs = lpeg.Cs(spce^-1 * bsqbracketsii * bcbracesii * bsqbrackets^-1) 55local cmdargs = lpeg.Cs(spce^-1 * bsqbracketsii * bcbracesii * bsqbrackets^-1)
56local cmdargsnobs = lpeg.Cs(spce^-1 * bcbracesii)
57local arbargs = lpeg.Cs(spce^-1 * bsqbrackets^-1 * bcbraces) 57local arbargs = lpeg.Cs(spce^-1 * bsqbrackets^-1 * bcbraces)
58local baytargs = lpeg.Cs(spce * bcbraces * bsqbrackets^-1 * bcbraces) 58local baytargs = lpeg.Cs(spce * bcbraces * bsqbrackets^-1 * bcbraces)
59local arind = lpeg.Cs(dblbkslash * lpeg.P("arind") * spce^-1 * bsqbracketsii) 59local arind = lpeg.Cs(dblbkslash * lpeg.P("arind") * spce^-1 * bsqbracketsii)
60 60
61local function protectarb(str) 61local function protectarb(str)
62 -- \App[]{}{}
63 str = string.gsub(str, "(\\App%s?)(%b{})(%b{})", "%1[]%2%3")
64 str = gsub(str, lpeg.P("\\App") * spcenc^-1 * bsqbrackets * bcbraces * bcbraces,
65 function(opt, lem, rdg)
66 opt = string.sub(opt, 2, -2)
67 lem = string.sub(lem, 2, -2)
68 rdg = string.sub(rdg, 2, -2)
69 if opt == ""
70 then
71 return string.format("\\app{%s%s}", lem, rdg)
72 else
73 return string.format("\\app[%s]{%s%s}", opt, lem, rdg)
74 end
75 end)
62 str = string.gsub(str, "(\\arb%s?)(%[.-%])(%b{})", "\\al@brk{\\arb%2%3}") 76 str = string.gsub(str, "(\\arb%s?)(%[.-%])(%b{})", "\\al@brk{\\arb%2%3}")
63 str = string.gsub(str, "(\\LR%s?)(%b{})", "\\@LR%2") 77 str = string.gsub(str, "(\\LR%s?)(%b{})", "\\@LR%2")
64 str = string.gsub(str, "(\\RL%s?)(%b{})", "\\@RL%2") 78 str = string.gsub(str, "(\\RL%s?)(%b{})", "\\@RL%2")
@@ -90,11 +104,11 @@ local outofarb = {
90local albrkcmds = { 104local albrkcmds = {
91 "begin", 105 "begin",
92 "end", 106 "end",
93 "par",
94 "LRmarginpar", 107 "LRmarginpar",
95 "arbmark", 108 "arbmark",
96 "abjad", 109 "abjad",
97 "ayah" 110 "ayah",
111 "SetArbNumbers"
98} 112}
99local brkcmds = {} 113local brkcmds = {}
100 114
@@ -120,13 +134,15 @@ function arabluatex.mkarbbreak(str, opt)
120end 134end
121 135
122local function breakcmd(str) 136local function breakcmd(str)
137 -- \par
138 str = gsub(str, dblbkslash * lpeg.Cs("par") * cmdargsnobs, "\\al@brk{%1%2%3}")
123 -- process \item[], then \item[] 139 -- process \item[], then \item[]
124 str = string.gsub(str, "\\(item.?)(%b[])", 140 str = string.gsub(str, "\\(item.?)(%b[])",
125 function(tag, body) 141 function(tag, body)
126 body = string.sub(body, 2, -2) 142 body = string.sub(body, 2, -2)
127 return string.format("\\al@brk{\\item[\\arb{%s}] }", body) 143 return string.format("\\al@brk{\\item[\\arb{%s}] }", body)
128 end) 144 end)
129 str = string.gsub(str, "(\\item)(%s+)", "%1{}%2") 145 str = string.gsub(str, "(\\item)(%s+)", "%1{}%2")
130 -- \textcolor 146 -- \textcolor
131 str = string.gsub(str, "\\(textcolor%s?)(%b{})(%b{})", 147 str = string.gsub(str, "\\(textcolor%s?)(%b{})(%b{})",
132 function(tag, bodycolor, bodytext) 148 function(tag, bodycolor, bodytext)
@@ -170,19 +186,38 @@ local function holdcmd(str)
170 return str 186 return str
171end 187end
172 188
173local function arbnum(str) 189local indorarbnum = "Indian"
190
191function arabluatex.setnums(opt)
192 if opt == "Indian"
193 then
194 indorarbnum = "Indian"
195 else
196 indorarbnum = "Arabic"
197 end
198end
199
200local function arbnum(str) -- not used, see below
174 str = string.gsub(str, "([0-9%,%-%/]+)", function(num) 201 str = string.gsub(str, "([0-9%,%-%/]+)", function(num)
175 return string.reverse(num) 202 return string.reverse(num)
176 end) 203 end)
177 return str 204 return str
178end 205end
179 206
180local function indnum(str) 207local function indnum(str, dir)
181 str = string.gsub(str, "([0-9%,%-%/]+)", function(num) 208 if dir == "ltr"
182 return string.reverse(num) 209 then
183 end) 210 -- do nothing
184 for i = 1,#numbers do 211 else
185 str = string.gsub(str, numbers[i].a, numbers[i].b) 212 str = string.gsub(str, "([0-9%,%-%/]+)", function(num)
213 return string.reverse(num)
214 end)
215 end
216 if indorarbnum == "Indian"
217 then
218 for i = 1,#numbers do
219 str = string.gsub(str, numbers[i].a, numbers[i].b)
220 end
186 end 221 end
187 return str 222 return str
188end 223end
@@ -276,6 +311,16 @@ local function takeoutarb(str)
276 return str 311 return str
277end 312end
278 313
314-- I owe this function to M. Krüger and U. Fischer. Thank to them both.
315local function getrenderer()
316 local a = nil
317 if font.getfont(font.current()).specification
318 then
319 a=font.getfont(font.current()).specification.features.normal.mode
320 end
321 return a
322end
323
279local function voc(str, rules) 324local function voc(str, rules)
280 str = string.gsub(str, "\\arb(%b{})", function(inside) 325 str = string.gsub(str, "\\arb(%b{})", function(inside)
281 inside = string.sub(inside, 2, -2) 326 inside = string.sub(inside, 2, -2)
@@ -311,8 +356,16 @@ local function voc(str, rules)
311 for i = 1,#shortv do 356 for i = 1,#shortv do
312 inside = string.gsub(inside, shortv[i].a, shortv[i].b) 357 inside = string.gsub(inside, shortv[i].a, shortv[i].b)
313 end 358 end
314 for i = 1,#punctuation do 359 if getrenderer() == "harf"
315 inside = string.gsub(inside, punctuation[i].a, punctuation[i].b) 360 then
361 for i = 1,#punctuationhb do
362 inside = string.gsub(inside, punctuationhb[i].a,
363 punctuationhb[i].b)
364 end
365 else
366 for i = 1,#punctuation do
367 inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
368 end
316 end 369 end
317 for i = 1,#null do 370 for i = 1,#null do
318 inside = string.gsub(inside, null[i].a, null[i].b) 371 inside = string.gsub(inside, null[i].a, null[i].b)
@@ -347,8 +400,16 @@ local function voceasy(str)
347 for i = 1,#shortv do 400 for i = 1,#shortv do
348 inside = string.gsub(inside, shortv[i].a, shortv[i].b) 401 inside = string.gsub(inside, shortv[i].a, shortv[i].b)
349 end 402 end
350 for i = 1,#punctuation do 403 if getrenderer() == "harf"
351 inside = string.gsub(inside, punctuation[i].a, punctuation[i].b) 404 then
405 for i = 1,#punctuationhb do
406 inside = string.gsub(inside, punctuationhb[i].a,
407 punctuationhb[i].b)
408 end
409 else
410 for i = 1,#punctuation do
411 inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
412 end
352 end 413 end
353 for i = 1,#null do 414 for i = 1,#null do
354 inside = string.gsub(inside, null[i].a, null[i].b) 415 inside = string.gsub(inside, null[i].a, null[i].b)
@@ -400,8 +461,16 @@ local function fullvoc(str, rules)
400 for i = 1,#shortv do 461 for i = 1,#shortv do
401 inside = string.gsub(inside, shortv[i].a, shortv[i].b) 462 inside = string.gsub(inside, shortv[i].a, shortv[i].b)
402 end 463 end
403 for i = 1,#punctuation do 464 if getrenderer() == "harf"
404 inside = string.gsub(inside, punctuation[i].a, punctuation[i].b) 465 then
466 for i = 1,#punctuationhb do
467 inside = string.gsub(inside, punctuationhb[i].a,
468 punctuationhb[i].b)
469 end
470 else
471 for i = 1,#punctuation do
472 inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
473 end
405 end 474 end
406 for i = 1,#null do 475 for i = 1,#null do
407 inside = string.gsub(inside, null[i].a, null[i].b) 476 inside = string.gsub(inside, null[i].a, null[i].b)
@@ -448,8 +517,16 @@ local function fullvoceasy(str, rules)
448 for i = 1,#shortv do 517 for i = 1,#shortv do
449 inside = string.gsub(inside, shortv[i].a, shortv[i].b) 518 inside = string.gsub(inside, shortv[i].a, shortv[i].b)
450 end 519 end
451 for i = 1,#punctuation do 520 if getrenderer() == "harf"
452 inside = string.gsub(inside, punctuation[i].a, punctuation[i].b) 521 then
522 for i = 1,#punctuationhb do
523 inside = string.gsub(inside, punctuationhb[i].a,
524 punctuationhb[i].b)
525 end
526 else
527 for i = 1,#punctuation do
528 inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
529 end
453 end 530 end
454 for i = 1,#null do 531 for i = 1,#null do
455 inside = string.gsub(inside, null[i].a, null[i].b) 532 inside = string.gsub(inside, null[i].a, null[i].b)
@@ -484,8 +561,16 @@ local function novoc(str)
484 for i = 1,#shortvnv do 561 for i = 1,#shortvnv do
485 inside = string.gsub(inside, shortvnv[i].a, shortvnv[i].b) 562 inside = string.gsub(inside, shortvnv[i].a, shortvnv[i].b)
486 end 563 end
487 for i = 1,#punctuation do 564 if getrenderer() == "harf"
488 inside = string.gsub(inside, punctuation[i].a, punctuation[i].b) 565 then
566 for i = 1,#punctuationhb do
567 inside = string.gsub(inside, punctuationhb[i].a,
568 punctuationhb[i].b)
569 end
570 else
571 for i = 1,#punctuation do
572 inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
573 end
489 end 574 end
490 for i = 1,#null do 575 for i = 1,#null do
491 inside = string.gsub(inside, null[i].a, null[i].b) 576 inside = string.gsub(inside, null[i].a, null[i].b)
@@ -520,8 +605,16 @@ local function novoceasy(str)
520 for i = 1,#shortvnv do 605 for i = 1,#shortvnv do
521 inside = string.gsub(inside, shortvnv[i].a, shortvnv[i].b) 606 inside = string.gsub(inside, shortvnv[i].a, shortvnv[i].b)
522 end 607 end
523 for i = 1,#punctuation do 608 if getrenderer() == "harf"
524 inside = string.gsub(inside, punctuation[i].a, punctuation[i].b) 609 then
610 for i = 1,#punctuationhb do
611 inside = string.gsub(inside, punctuationhb[i].a,
612 punctuationhb[i].b)
613 end
614 else
615 for i = 1,#punctuation do
616 inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
617 end
525 end 618 end
526 for i = 1,#null do 619 for i = 1,#null do
527 inside = string.gsub(inside, null[i].a, null[i].b) 620 inside = string.gsub(inside, null[i].a, null[i].b)
@@ -532,13 +625,19 @@ local function novoceasy(str)
532 return str 625 return str
533end 626end
534 627
535local trfontinfo = "\\altrfont " 628local function transdmg(str, mode, rules)
536
537local function transdmg(str, rules)
538 str = string.gsub(str, "\\arb(%b{})", function(inside) 629 str = string.gsub(str, "\\arb(%b{})", function(inside)
539 inside = string.sub(inside, 2, -2) 630 inside = string.sub(inside, 2, -2)
540 for i = 1,#hamzatrdmg do 631 if mode == "dmg"
541 inside = string.gsub(inside, hamzatrdmg[i].a, hamzatrdmg[i].b) 632 then
633 for i = 1,#hamzatrnoinitialdmg do
634 inside = string.gsub(inside, hamzatrnoinitialdmg[i].a, hamzatrnoinitialdmg[i].b)
635 end
636 elseif mode == "dmg+"
637 then
638 for i = 1,#hamzatrdmg do
639 inside = string.gsub(inside, hamzatrdmg[i].a, hamzatrdmg[i].b)
640 end
542 end 641 end
543 for i = 1,#tanwintrdmg do 642 for i = 1,#tanwintrdmg do
544 inside = string.gsub(inside, tanwintrdmg[i].a, tanwintrdmg[i].b) 643 inside = string.gsub(inside, tanwintrdmg[i].a, tanwintrdmg[i].b)
@@ -569,7 +668,7 @@ local function transdmg(str, rules)
569 for i = 1,#nulltr do 668 for i = 1,#nulltr do
570 inside = string.gsub(inside, nulltr[i].a, nulltr[i].b) 669 inside = string.gsub(inside, nulltr[i].a, nulltr[i].b)
571 end 670 end
572 return string.format("{%s%s}", trfontinfo, inside) 671 return string.format("\\altrfont{}%s", inside)
573 end) 672 end)
574 return str 673 return str
575end 674end
@@ -607,7 +706,7 @@ local function transloc(str)
607 for i = 1,#nulltr do 706 for i = 1,#nulltr do
608 inside = string.gsub(inside, nulltr[i].a, nulltr[i].b) 707 inside = string.gsub(inside, nulltr[i].a, nulltr[i].b)
609 end 708 end
610 return string.format("{%s%s}", trfontinfo, inside) 709 return string.format("\\altrfont{}%s", inside)
611 end) 710 end)
612 return str 711 return str
613end 712end
@@ -642,7 +741,7 @@ local function transarabica(str)
642 for i = 1,#nulltr do 741 for i = 1,#nulltr do
643 inside = string.gsub(inside, nulltr[i].a, nulltr[i].b) 742 inside = string.gsub(inside, nulltr[i].a, nulltr[i].b)
644 end 743 end
645 return string.format("{%s%s}", trfontinfo, inside) 744 return string.format("\\altrfont{}%s", inside)
646 end) 745 end)
647 return str 746 return str
648end 747end
@@ -741,16 +840,16 @@ local function processarbtoutf(str)
741 end) 840 end)
742 return string.format("%s{%s}", tag, body) 841 return string.format("%s{%s}", tag, body)
743 end) 842 end)
744 str = string.gsub(str, "(\\bayt)%s?(%b{})(%b[])(%b{})", function(tag, argi, argii, argiii) 843 str = string.gsub(str, "(\\bayt)%s?(%+?)(%b{})(%b[])(%b{})", function(tag, plus, argi, argii, argiii)
745 argi = string.sub(argi, 2, -2) 844 argi = string.sub(argi, 2, -2)
746 argii = string.sub(argii, 2, -2) 845 argii = string.sub(argii, 2, -2)
747 argiii = string.sub(argiii, 2, -2) 846 argiii = string.sub(argiii, 2, -2)
748 return string.format("%s*{\\arb{%s}}[\\arb{%s}]{\\arb{%s}}", tag, argi, argii, argiii) 847 return string.format("%s%s*{\\arb{%s}}[\\arb{%s}]{\\arb{%s}}", tag, argi, argii, argiii)
749 end) 848 end)
750 str = string.gsub(str, "(\\bayt)%s?(%b{})(%b{})", function(tag, argi, argii) 849 str = string.gsub(str, "(\\bayt)%s?(%+?)(%b{})(%b{})", function(tag, plus, argi, argii)
751 argi = string.sub(argi, 2, -2) 850 argi = string.sub(argi, 2, -2)
752 argii = string.sub(argii, 2, -2) 851 argii = string.sub(argii, 2, -2)
753 return string.format("%s*{\\arb{%s}}{\\arb{%s}}", tag, argi, argii) 852 return string.format("%s%s*{\\arb{%s}}{\\arb{%s}}", tag, plus, argi, argii)
754 end) 853 end)
755 str = string.gsub(str, "(\\prname)%s?(%b{})", function(tag, body) 854 str = string.gsub(str, "(\\prname)%s?(%b{})", function(tag, body)
756 body = string.sub(body, 2, -2) 855 body = string.sub(body, 2, -2)
@@ -843,6 +942,7 @@ function arabluatex.closestream()
843 local o = io.open(tex.jobname..utffilesuffix..".tex", "w") 942 local o = io.open(tex.jobname..utffilesuffix..".tex", "w")
844 local t = f:read("*a") 943 local t = f:read("*a")
845 t = string.gsub(t, "\\arabicfont{}", "") 944 t = string.gsub(t, "\\arabicfont{}", "")
945 t = string.gsub(t, "\\altrfont{}", "")
846 t = string.gsub(t, "\\par ", "\n\n") 946 t = string.gsub(t, "\\par ", "\n\n")
847 t = string.gsub(t, "(\\@al@ob)", "{") 947 t = string.gsub(t, "(\\@al@ob)", "{")
848 t = string.gsub(t, "(\\@al@cb@sp)", "} ") 948 t = string.gsub(t, "(\\@al@cb@sp)", "} ")
@@ -1005,8 +1105,8 @@ function arabluatex.processtrans(str, mode, rules, scheme)
1005 if scheme == "buckwalter" then 1105 if scheme == "buckwalter" then
1006 str = processbuckw(str) 1106 str = processbuckw(str)
1007 end 1107 end
1008 if mode == "dmg" then 1108 if mode == "dmg" or mode == "dmg+" then
1009 str = transdmg(str, rules) 1109 str = transdmg(str, mode, rules)
1010 elseif mode == "loc" then 1110 elseif mode == "loc" then
1011 str = transloc(str) 1111 str = transloc(str)
1012 elseif mode == "arabica" then 1112 elseif mode == "arabica" then
@@ -1082,6 +1182,7 @@ function arabluatex.uc(str)
1082 body = string.sub(body, 2, -2) 1182 body = string.sub(body, 2, -2)
1083 return string.format("%s", body) 1183 return string.format("%s", body)
1084 end) 1184 end)
1185 str = string.gsub(str, "\\altrfont%s?{}", "")
1085 str = string.gsub(str, "{", "\\@al@ob") 1186 str = string.gsub(str, "{", "\\@al@ob")
1086 str = string.gsub(str, "} ", "\\@al@cb@sp ") 1187 str = string.gsub(str, "} ", "\\@al@cb@sp ")
1087 str = string.gsub(str, "}", "\\@al@cb") 1188 str = string.gsub(str, "}", "\\@al@cb")
@@ -1136,6 +1237,7 @@ function arabluatex.uc(str)
1136 str = string.gsub(str, "\\@al@ob", "{") 1237 str = string.gsub(str, "\\@al@ob", "{")
1137 str = string.gsub(str, "\\@al@cb@sp ", "} ") 1238 str = string.gsub(str, "\\@al@cb@sp ", "} ")
1138 str = string.gsub(str, "\\@al@cb", "}") 1239 str = string.gsub(str, "\\@al@cb", "}")
1240 str = "\\altrfont{}"..str
1139 if export_utf == "yes" then 1241 if export_utf == "yes" then
1140 tofile = str 1242 tofile = str
1141 arabluatex.tooutfile(tofile) 1243 arabluatex.tooutfile(tofile)
@@ -1175,8 +1277,14 @@ function arabluatex.abjadify(n)
1175end 1277end
1176 1278
1177function arabluatex.abraces(str) 1279function arabluatex.abraces(str)
1178 if tex.textdir == "TRT" then 1280 if tex.textdir == "TRT"
1179 str = "\\}"..str.."\\{" 1281 then
1282 if getrenderer() == "harf"
1283 then
1284 str = "\\{"..str.."\\}"
1285 else
1286 str = "\\}"..str.."\\{"
1287 end
1180 elseif tex.textdir == "TLT" then 1288 elseif tex.textdir == "TLT" then
1181 str = "\\{"..str.."\\}" 1289 str = "\\{"..str.."\\}"
1182 end 1290 end
@@ -1199,7 +1307,9 @@ end
1199function arabluatex.ayah(str) 1307function arabluatex.ayah(str)
1200 if tonumber(str) ~= nil and str.len(str) < 4 then 1308 if tonumber(str) ~= nil and str.len(str) < 4 then
1201 if tex.textdir == "TRT" then 1309 if tex.textdir == "TRT" then
1202 str = indnum(str).."^^^^06dd" 1310 -- end of ayah is typeset LTR and the number comes after the
1311 -- sign (see https://github.com/aliftype/amiri/issues/263#issuecomment-1872979252)
1312 str = "{\\textdir TLT ".."^^^^06dd"..indnum(str, "ltr").."}"
1203 elseif tex.textdir == "TLT" then 1313 elseif tex.textdir == "TLT" then
1204 str = "\\arb[trans]{("..str..")}" 1314 str = "\\arb[trans]{("..str..")}"
1205 end 1315 end