arabluatex.lua 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857
  1. --[[
  2. This file is part of the `arabluatex' package
  3. ArabLuaTeX -- Processing ArabTeX notation under LuaLaTeX
  4. Copyright (C) 2016--2018 Robert Alessi
  5. Please send error reports and suggestions for improvements to Robert
  6. Alessi <alessi@robertalessi.net>
  7. This program is free software: you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation, either version 3 of the License, or
  10. (at your option) any later version.
  11. This program is distributed in the hope that it will be useful, but
  12. WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with this program. If not, see
  17. <http://www.gnu.org/licenses/>.
  18. --]]
  19. require("arabluatex_voc")
  20. require("arabluatex_fullvoc")
  21. require("arabluatex_novoc")
  22. require("arabluatex_trans")
  23. -- lpeg equivalent for string.gsub()
  24. local function gsub(s, patt, repl)
  25. patt = lpeg.P(patt)
  26. patt = lpeg.Cs((patt / repl + 1)^0)
  27. return lpeg.match(patt, s)
  28. end
  29. -- makeatletter, makeatother
  30. local atletter = "\\makeatletter{}"
  31. local atother = "\\makeatother{}"
  32. -- some basic patterns:
  33. local ascii = lpeg.R("az", "AZ", "@@")
  34. local dblbkslash = lpeg.Cs("\\")
  35. local bsqbrackets = lpeg.Cs{ "[" * ((1 - lpeg.S"[]") + lpeg.V(1))^0 * "]" }
  36. local bcbraces = lpeg.Cs{ "{" * ((1 - lpeg.S"{}") + lpeg.V(1))^0 * "}" }
  37. local spce = lpeg.Cs(" ")
  38. local spcenc = lpeg.P(" ")
  39. local cmdstar = lpeg.Cs(spce * lpeg.P("*"))
  40. local bsqbracketsii = lpeg.Cs(bsqbrackets^-2)
  41. local bcbracesii = lpeg.Cs(bcbraces^-2)
  42. local cmd = lpeg.Cs(dblbkslash * ascii^1 * cmdstar^-1)
  43. local cmdargs = lpeg.Cs(spce * bsqbracketsii * bcbracesii * bsqbrackets^-1)
  44. local function protectarb(str)
  45. str = string.gsub(str, "(\\arb%s?)(%[.-%])(%b{})", "\\@arb%2%3")
  46. str = string.gsub(str, "(\\LR%s?)(%b{})", "\\@LR%2")
  47. str = string.gsub(str, "(\\RL%s?)(%b{})", "\\@RL%2")
  48. return str
  49. end
  50. local function unprotectarb(str)
  51. str = string.gsub(str, "(\\@arb)(%[.-%])(%b{})", "\\arb%2%3")
  52. str = string.gsub(str, "(\\@LR)(%b{})", "\\LR%2")
  53. str = string.gsub(str, "(\\@RL)(%b{})", "\\RL%2")
  54. str = gsub(str, lpeg.Cs("\\al@brk") * bcbraces, function(tag, body)
  55. body = string.sub(body, 2, -2)
  56. return string.format("%s", body)
  57. end)
  58. return str
  59. end
  60. -- the following is to be taken out of \arb{}
  61. local outofarb = {
  62. "LRfootnote",
  63. "RLfootnote",
  64. "edtext",
  65. "pstart",
  66. "pend"
  67. }
  68. -- commands the arguments of which must not be processed by arabluatex
  69. -- inside \arb{}. 'albrkcmds' is what is set by default. 'brkcmds'
  70. -- collects the commands set in the preamble with \MkArbBreak{}
  71. local albrkcmds = {
  72. "begin",
  73. "end",
  74. "par",
  75. "LRmarginpar",
  76. "arbmark",
  77. "abjad"
  78. }
  79. local brkcmds = {}
  80. function mkarbbreak(str, opt)
  81. str = str ..","
  82. str = string.gsub(str, "%s+", "")
  83. local fieldstart = 1
  84. if opt == "dflt" then
  85. repeat
  86. local nexti = string.find(str, "%,", fieldstart)
  87. table.insert(brkcmds, string.sub(str, fieldstart, nexti-1))
  88. fieldstart = nexti +1
  89. until fieldstart > string.len(str)
  90. return brkcmds
  91. elseif opt == "out" then
  92. repeat
  93. local nexti = string.find(str, "%,", fieldstart)
  94. table.insert(outofarb, string.sub(str, fieldstart, nexti-1))
  95. fieldstart = nexti +1
  96. until fieldstart > string.len(str)
  97. return outofarb
  98. end
  99. end
  100. local function breakcmd(str)
  101. -- process \item[], then \item[]
  102. str = string.gsub(str, "\\(item.?)(%b[])",
  103. function(tag, body)
  104. body = string.sub(body, 2, -2)
  105. return string.format("\\al@brk{\\item[\\arb{%s}] }", body)
  106. end)
  107. str = string.gsub(str, "(\\item)(%s+)", "%1{}%2")
  108. -- \textcolor
  109. str = string.gsub(str, "\\(textcolor%s?)(%b{})(%b{})",
  110. function(tag, bodycolor, bodytext)
  111. bodycolor = string.sub(bodycolor, 2, -2)
  112. bodytext = string.sub(bodytext, 2, -2)
  113. return string.format("\\al@brk{\\%s{%s}{\\arb{%s}}}", tag, bodycolor, bodytext)
  114. end)
  115. -- commands set by default in outofarb
  116. for i = 1,#outofarb do
  117. str = gsub(str, dblbkslash * lpeg.Cs(outofarb[i]) * cmdargs, "}%1%2%3\\arb{")
  118. end
  119. -- commands set by default in albrkcmds
  120. for i = 1,#albrkcmds do
  121. str = gsub(str, dblbkslash * lpeg.Cs(albrkcmds[i]) * cmdargs, "\\al@brk{%1%2%3}")
  122. end
  123. -- user commands (brkcmds)
  124. if next(brkcmds) == nil then
  125. -- nothing to do
  126. else
  127. for i = 1,#brkcmds do
  128. str = gsub(str, dblbkslash * lpeg.Cs(brkcmds[i]) * cmdargs, "\\al@brk{%1%2%3}")
  129. end
  130. end
  131. return str
  132. end
  133. local function holdcmd(str)
  134. str = gsub(str, lpeg.Cs("\\arb") * bcbraces, function(tag, body)
  135. body = string.sub(body, 2, -2)
  136. body = gsub(body, cmd * spcenc^-1 * bsqbracketsii * spcenc^-1 * bcbraces, function(btag, bopt, bbody)
  137. bbody = string.sub(bbody, 2, -2)
  138. if string.find(btag, "@") then
  139. return holdcmd(string.format("}%s%s{%s}\\arb{", btag, bopt, bbody))
  140. else
  141. return holdcmd(string.format("}%s%s{\\arb{%s}}\\arb{", btag, bopt, bbody))
  142. end
  143. end)
  144. return string.format("%s{%s}", tag, body)
  145. end)
  146. str = string.gsub(str, "\\arb{}", "")
  147. return str
  148. end
  149. local function arbnum(str)
  150. str = string.gsub(str, "([0-9%,%-%/]+)", function(num)
  151. return string.reverse(num)
  152. end)
  153. return str
  154. end
  155. local function indnum(str)
  156. str = string.gsub(str, "([0-9%,%-%/]+)", function(num)
  157. return string.reverse(num)
  158. end)
  159. for i = 1,#numbers do
  160. str = string.gsub(str, numbers[i].a, numbers[i].b)
  161. end
  162. return str
  163. end
  164. local function processdiscretionary(str)
  165. str = string.gsub(str, "\\%-", "\\-{}")
  166. return str
  167. end
  168. local function processarbnull(str, scheme)
  169. if scheme == "buckwalter" then
  170. str = string.gsub(str, "(\\arbnull.?)(%b{})", function(tag, body)
  171. body = string.sub(body, 2, -2)
  172. return string.format("P%sP", body)
  173. end)
  174. else
  175. str = string.gsub(str, "(\\arbnull.?)(%b{})", function(tag, body)
  176. body = string.sub(body, 2, -2)
  177. return string.format("O%sO", body)
  178. end)
  179. end
  180. return str
  181. end
  182. local function takeoutabjad(str)
  183. str = string.gsub(str, "(\\abjad.?)(%b{})", function(tag, body)
  184. body = string.sub(body, 2, -2)
  185. return string.format("%s", body)
  186. end)
  187. return str
  188. end
  189. local function takeoutcapetc(str)
  190. str = string.gsub(str, "(\\arb.?%[trans%])(%b{})", function(tag, body)
  191. body = string.sub(body, 2, -2)
  192. body = string.gsub(body, "(\\uc%s?)(%b{})", "\\Uc%2")
  193. return string.format("%s{%s}", tag, body)
  194. end)
  195. str = string.gsub(str, "(\\arbup.?)(%b{})", function(tag, body)
  196. body = string.sub(body, 2, -2)
  197. return string.format("%s", body)
  198. end)
  199. str = string.gsub(str, "(\\uc.?)(%b{})", function(tag, body)
  200. body = string.sub(body, 2, -2)
  201. return string.format("%s", body)
  202. end)
  203. str = string.gsub(str, "\\linebreak", "")
  204. str = string.gsub(str, "\\%-", "")
  205. return str
  206. end
  207. local function checkwrnested(str)
  208. for i = 1,#outofarb do
  209. str = gsub(str, dblbkslash * lpeg.Cs(lpeg.P("LR") + lpeg.P("RL")) * cmdargs,
  210. function(prefix, tag, body)
  211. body = string.sub(body, 2, -2)
  212. if string.find(body, "\\"..outofarb[i]) then
  213. return atletter.."\\al@wrong@nesting{}"..atother
  214. else
  215. -- nothing to do, so proceed.
  216. end
  217. end)
  218. end
  219. return str
  220. end
  221. local function takeoutarb(str)
  222. str = checkwrnested(str)
  223. for i = 1,#outofarb do
  224. str = gsub(str, dblbkslash * lpeg.Cs(outofarb[i]) * cmdargs,
  225. function(prefix, tag, body)
  226. body = gsub(body, lpeg.P("\\arb"), "\\@rb")
  227. return string.format("%s%s%s", prefix, tag, body)
  228. end)
  229. end
  230. str = string.gsub(str, "(\\arb%s?)(%b{})", function(tag, body)
  231. body = string.sub(body, 2, -2)
  232. return string.format("%s", body)
  233. end)
  234. str = string.gsub(str, "\\@rb", "\\arb")
  235. str = "\\arb{"..str.."}"
  236. return str
  237. end
  238. local function voc(str, rules)
  239. str = string.gsub(str, "\\arb(%b{})", function(inside)
  240. inside = string.sub(inside, 2, -2)
  241. for i = 1,#hamza do
  242. inside = string.gsub(inside, hamza[i].a, hamza[i].b)
  243. end
  244. if rules == "idgham" then
  245. for i = 1,#tanwin do
  246. inside = string.gsub(inside, tanwin[i].a, tanwin[i].b)
  247. end
  248. else
  249. for i = 1,#tanwineasy do
  250. inside = string.gsub(inside, tanwineasy[i].a, tanwineasy[i].b)
  251. end
  252. end
  253. for i = 1,#trigraphs do
  254. inside = string.gsub(inside, trigraphs[i].a, trigraphs[i].b)
  255. end
  256. if rules == "idgham" then
  257. for i = 1,#idgham do
  258. inside = string.gsub(inside, idgham[i].a, idgham[i].b)
  259. end
  260. end
  261. for i = 1,#digraphs do
  262. inside = string.gsub(inside, digraphs[i].a, digraphs[i].b)
  263. end
  264. for i = 1,#single do
  265. inside = string.gsub(inside, single[i].a, single[i].b)
  266. end
  267. for i = 1,#longv do
  268. inside = string.gsub(inside, longv[i].a, longv[i].b)
  269. end
  270. for i = 1,#shortv do
  271. inside = string.gsub(inside, shortv[i].a, shortv[i].b)
  272. end
  273. for i = 1,#punctuation do
  274. inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
  275. end
  276. for i = 1,#null do
  277. inside = string.gsub(inside, null[i].a, null[i].b)
  278. end
  279. inside = indnum(inside)
  280. return string.format("\\arabicfont{}%s", inside)
  281. end)
  282. return str
  283. end
  284. local function voceasy(str)
  285. str = string.gsub(str, "\\arb(%b{})", function(inside)
  286. inside = string.sub(inside, 2, -2)
  287. for i = 1,#hamzaeasy do
  288. inside = string.gsub(inside, hamzaeasy[i].a, hamzaeasy[i].b)
  289. end
  290. for i = 1,#tanwineasy do
  291. inside = string.gsub(inside, tanwineasy[i].a, tanwineasy[i].b)
  292. end
  293. for i = 1,#trigraphseasy do
  294. inside = string.gsub(inside, trigraphseasy[i].a, trigraphseasy[i].b)
  295. end
  296. for i = 1,#digraphs do
  297. inside = string.gsub(inside, digraphs[i].a, digraphs[i].b)
  298. end
  299. for i = 1,#single do
  300. inside = string.gsub(inside, single[i].a, single[i].b)
  301. end
  302. for i = 1,#longv do
  303. inside = string.gsub(inside, longv[i].a, longv[i].b)
  304. end
  305. for i = 1,#shortv do
  306. inside = string.gsub(inside, shortv[i].a, shortv[i].b)
  307. end
  308. for i = 1,#punctuation do
  309. inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
  310. end
  311. for i = 1,#null do
  312. inside = string.gsub(inside, null[i].a, null[i].b)
  313. end
  314. inside = indnum(inside)
  315. return string.format("\\arabicfont{}%s", inside)
  316. end)
  317. return str
  318. end
  319. local function fullvoc(str, rules)
  320. str = string.gsub(str, "\\arb(%b{})", function(inside)
  321. inside = string.sub(inside, 2, -2)
  322. for i = 1,#hamzafv do
  323. inside = string.gsub(inside, hamzafv[i].a, hamzafv[i].b)
  324. end
  325. if rules == "idgham" then
  326. for i = 1,#tanwinfv do
  327. inside = string.gsub(inside, tanwinfv[i].a, tanwinfv[i].b)
  328. end
  329. else
  330. for i = 1,#tanwinfveasy do
  331. inside = string.gsub(inside, tanwinfveasy[i].a, tanwinfveasy[i].b)
  332. end
  333. end
  334. for i = 1,#trigraphsfv do
  335. inside = string.gsub(inside, trigraphsfv[i].a, trigraphsfv[i].b)
  336. end
  337. if rules == "idgham" then
  338. for i = 1,#idgham do
  339. inside = string.gsub(inside, idgham[i].a, idgham[i].b)
  340. end
  341. end
  342. if rules == "idgham" then
  343. for i = 1,#digraphsfvidgham do
  344. inside = string.gsub(inside, digraphsfvidgham[i].a, digraphsfvidgham[i].b)
  345. end
  346. else
  347. for i = 1,#digraphsfv do
  348. inside = string.gsub(inside, digraphsfv[i].a, digraphsfv[i].b)
  349. end
  350. end
  351. for i = 1,#singlefv do
  352. inside = string.gsub(inside, singlefv[i].a, singlefv[i].b)
  353. end
  354. for i = 1,#longv do
  355. inside = string.gsub(inside, longv[i].a, longv[i].b)
  356. end
  357. for i = 1,#shortv do
  358. inside = string.gsub(inside, shortv[i].a, shortv[i].b)
  359. end
  360. for i = 1,#punctuation do
  361. inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
  362. end
  363. for i = 1,#null do
  364. inside = string.gsub(inside, null[i].a, null[i].b)
  365. end
  366. inside = indnum(inside)
  367. return string.format("\\arabicfont{}%s", inside)
  368. end)
  369. return str
  370. end
  371. local function fullvoceasy(str, rules)
  372. str = string.gsub(str, "\\arb(%b{})", function(inside)
  373. inside = string.sub(inside, 2, -2)
  374. for i = 1,#hamzafveasy do
  375. inside = string.gsub(inside, hamzafveasy[i].a, hamzafveasy[i].b)
  376. end
  377. for i = 1,#tanwinfveasy do
  378. inside = string.gsub(inside, tanwinfveasy[i].a, tanwinfveasy[i].b)
  379. end
  380. for i = 1,#trigraphsfveasy do
  381. inside = string.gsub(inside, trigraphsfveasy[i].a, trigraphsfveasy[i].b)
  382. end
  383. if rules == "nosukun" then
  384. for i = 1,#digraphsfveasy do
  385. inside = string.gsub(inside, digraphsfveasy[i].a, digraphsfveasy[i].b)
  386. end
  387. else
  388. for i = 1,#digraphsfv do
  389. inside = string.gsub(inside, digraphsfv[i].a, digraphsfv[i].b)
  390. end
  391. end
  392. if rules == "nosukun" then
  393. for i = 1,#singlefveasy do
  394. inside = string.gsub(inside, singlefveasy[i].a, singlefveasy[i].b)
  395. end
  396. else
  397. for i = 1,#singlefv do
  398. inside = string.gsub(inside, singlefv[i].a, singlefv[i].b)
  399. end
  400. end
  401. for i = 1,#longv do
  402. inside = string.gsub(inside, longv[i].a, longv[i].b)
  403. end
  404. for i = 1,#shortv do
  405. inside = string.gsub(inside, shortv[i].a, shortv[i].b)
  406. end
  407. for i = 1,#punctuation do
  408. inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
  409. end
  410. for i = 1,#null do
  411. inside = string.gsub(inside, null[i].a, null[i].b)
  412. end
  413. inside = indnum(inside)
  414. return string.format("\\arabicfont{}%s", inside)
  415. end)
  416. return str
  417. end
  418. local function novoc(str)
  419. str = string.gsub(str, "\\arb(%b{})", function(inside)
  420. inside = string.sub(inside, 2, -2)
  421. for i = 1,#hamza do
  422. inside = string.gsub(inside, hamza[i].a, hamza[i].b)
  423. end
  424. for i = 1,#tanwinnv do
  425. inside = string.gsub(inside, tanwinnv[i].a, tanwinnv[i].b)
  426. end
  427. for i = 1,#trigraphsnv do
  428. inside = string.gsub(inside, trigraphsnv[i].a, trigraphsnv[i].b)
  429. end
  430. for i = 1,#digraphs do
  431. inside = string.gsub(inside, digraphs[i].a, digraphs[i].b)
  432. end
  433. for i = 1,#single do
  434. inside = string.gsub(inside, single[i].a, single[i].b)
  435. end
  436. for i = 1,#longvnv do
  437. inside = string.gsub(inside, longvnv[i].a, longvnv[i].b)
  438. end
  439. for i = 1,#shortvnv do
  440. inside = string.gsub(inside, shortvnv[i].a, shortvnv[i].b)
  441. end
  442. for i = 1,#punctuation do
  443. inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
  444. end
  445. for i = 1,#null do
  446. inside = string.gsub(inside, null[i].a, null[i].b)
  447. end
  448. inside = indnum(inside)
  449. return string.format("\\arabicfont{}%s", inside)
  450. end)
  451. return str
  452. end
  453. local function novoceasy(str)
  454. str = string.gsub(str, "\\arb(%b{})", function(inside)
  455. inside = string.sub(inside, 2, -2)
  456. for i = 1,#hamzaeasy do
  457. inside = string.gsub(inside, hamzaeasy[i].a, hamzaeasy[i].b)
  458. end
  459. for i = 1,#tanwinnv do
  460. inside = string.gsub(inside, tanwinnv[i].a, tanwinnv[i].b)
  461. end
  462. for i = 1,#trigraphsnv do
  463. inside = string.gsub(inside, trigraphsnv[i].a, trigraphsnv[i].b)
  464. end
  465. for i = 1,#digraphs do
  466. inside = string.gsub(inside, digraphs[i].a, digraphs[i].b)
  467. end
  468. for i = 1,#single do
  469. inside = string.gsub(inside, single[i].a, single[i].b)
  470. end
  471. for i = 1,#longvnv do
  472. inside = string.gsub(inside, longvnv[i].a, longvnv[i].b)
  473. end
  474. for i = 1,#shortvnv do
  475. inside = string.gsub(inside, shortvnv[i].a, shortvnv[i].b)
  476. end
  477. for i = 1,#punctuation do
  478. inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
  479. end
  480. for i = 1,#null do
  481. inside = string.gsub(inside, null[i].a, null[i].b)
  482. end
  483. inside = indnum(inside)
  484. return string.format("\\arabicfont{}%s", inside)
  485. end)
  486. return str
  487. end
  488. local function transdmg(str, rules)
  489. str = string.gsub(str, "\\arb(%b{})", function(inside)
  490. inside = string.sub(inside, 2, -2)
  491. for i = 1,#hamzatrdmg do
  492. inside = string.gsub(inside, hamzatrdmg[i].a, hamzatrdmg[i].b)
  493. end
  494. for i = 1,#tanwintrdmg do
  495. inside = string.gsub(inside, tanwintrdmg[i].a, tanwintrdmg[i].b)
  496. end
  497. for i = 1,#trigraphstrdmg do
  498. inside = string.gsub(inside, trigraphstrdmg[i].a, trigraphstrdmg[i].b)
  499. end
  500. if rules == "idgham" then
  501. for i = 1,#idghamtrdmg do
  502. inside = string.gsub(inside, idghamtrdmg[i].a, idghamtrdmg[i].b)
  503. end
  504. end
  505. for i = 1,#digraphstrdmg do
  506. inside = string.gsub(inside, digraphstrdmg[i].a, digraphstrdmg[i].b)
  507. end
  508. for i = 1,#singletrdmg do
  509. inside = string.gsub(inside, singletrdmg[i].a, singletrdmg[i].b)
  510. end
  511. for i = 1,#longvtrdmg do
  512. inside = string.gsub(inside, longvtrdmg[i].a, longvtrdmg[i].b)
  513. end
  514. for i = 1,#shortvtrdmg do
  515. inside = string.gsub(inside, shortvtrdmg[i].a, shortvtrdmg[i].b)
  516. end
  517. for i = 1,#punctuationtr do
  518. inside = string.gsub(inside, punctuationtr[i].a, punctuationtr[i].b)
  519. end
  520. for i = 1,#nulltr do
  521. inside = string.gsub(inside, nulltr[i].a, nulltr[i].b)
  522. end
  523. return string.format("\\txtrans{%s}", inside)
  524. end)
  525. return str
  526. end
  527. local function transloc(str)
  528. str = string.gsub(str, "\\arb(%b{})", function(inside)
  529. inside = string.sub(inside, 2, -2)
  530. for i = 1,#hamzatrloc do
  531. inside = string.gsub(inside, hamzatrloc[i].a, hamzatrloc[i].b)
  532. end
  533. for i = 1,#tanwintrloc do
  534. inside = string.gsub(inside, tanwintrloc[i].a, tanwintrloc[i].b)
  535. end
  536. for i = 1,#trigraphstrloc do
  537. inside = string.gsub(inside, trigraphstrloc[i].a, trigraphstrloc[i].b)
  538. end
  539. for i = 1,#digraphstrloc do
  540. inside = string.gsub(inside, digraphstrloc[i].a, digraphstrloc[i].b)
  541. end
  542. for i = 1,#singletrloc do
  543. inside = string.gsub(inside, singletrloc[i].a, singletrloc[i].b)
  544. end
  545. for i = 1,#longvtrloc do
  546. inside = string.gsub(inside, longvtrloc[i].a, longvtrloc[i].b)
  547. end
  548. for i = 1,#shortvtrloc do
  549. inside = string.gsub(inside, shortvtrloc[i].a, shortvtrloc[i].b)
  550. end
  551. for i = 1,#finaltrloc do
  552. inside = string.gsub(inside, finaltrloc[i].a, finaltrloc[i].b)
  553. end
  554. for i = 1,#punctuationtr do
  555. inside = string.gsub(inside, punctuationtr[i].a, punctuationtr[i].b)
  556. end
  557. for i = 1,#nulltr do
  558. inside = string.gsub(inside, nulltr[i].a, nulltr[i].b)
  559. end
  560. return string.format("\\txtrans{%s}", inside)
  561. end)
  562. return str
  563. end
  564. local function transarabica(str)
  565. str = string.gsub(str, "\\arb(%b{})", function(inside)
  566. inside = string.sub(inside, 2, -2)
  567. for i = 1,#hamzatrarabica do
  568. inside = string.gsub(inside, hamzatrarabica[i].a, hamzatrarabica[i].b)
  569. end
  570. for i = 1,#tanwintrloc do
  571. inside = string.gsub(inside, tanwintrloc[i].a, tanwintrloc[i].b)
  572. end
  573. for i = 1,#trigraphstrarabica do
  574. inside = string.gsub(inside, trigraphstrarabica[i].a, trigraphstrarabica[i].b)
  575. end
  576. for i = 1,#digraphstrarabica do
  577. inside = string.gsub(inside, digraphstrarabica[i].a, digraphstrarabica[i].b)
  578. end
  579. for i = 1,#singletrarabica do
  580. inside = string.gsub(inside, singletrarabica[i].a, singletrarabica[i].b)
  581. end
  582. for i = 1,#longvtrarabica do
  583. inside = string.gsub(inside, longvtrarabica[i].a, longvtrarabica[i].b)
  584. end
  585. for i = 1,#shortvtrloc do
  586. inside = string.gsub(inside, shortvtrloc[i].a, shortvtrloc[i].b)
  587. end
  588. for i = 1,#punctuationtr do
  589. inside = string.gsub(inside, punctuationtr[i].a, punctuationtr[i].b)
  590. end
  591. for i = 1,#nulltr do
  592. inside = string.gsub(inside, nulltr[i].a, nulltr[i].b)
  593. end
  594. return string.format("\\txtrans{%s}", inside)
  595. end)
  596. return str
  597. end
  598. local function processbuckw(str)
  599. str = string.gsub(str, "\\arb(%b{})", function(inside)
  600. inside = string.sub(inside, 2, -2)
  601. for i = 1,#buckwalter do
  602. inside = string.gsub(inside, buckwalter[i].a, buckwalter[i].b)
  603. end
  604. return string.format("\\arb{%s}", inside)
  605. end)
  606. return str
  607. end
  608. function processvoc(str, rules, scheme)
  609. str = takeoutarb(str)
  610. str = processarbnull(str, scheme)
  611. str = takeoutcapetc(str)
  612. str = protectarb(str)
  613. str = breakcmd(str)
  614. str = holdcmd(str)
  615. if scheme == "buckwalter" then
  616. str = processbuckw(str)
  617. else end
  618. if rules == "easy" or rules == "easynosukun" then
  619. str = voceasy(str)
  620. elseif rules == "dflt" or rules == "idgham" then
  621. str = voc(str, rules)
  622. else end
  623. str = unprotectarb(str)
  624. return str
  625. end
  626. function processfullvoc(str, rules, scheme)
  627. str = takeoutarb(str)
  628. str = processarbnull(str, scheme)
  629. str = takeoutcapetc(str)
  630. str = protectarb(str)
  631. str = breakcmd(str)
  632. str = holdcmd(str)
  633. if scheme == "buckwalter" then
  634. str = processbuckw(str)
  635. else end
  636. if rules == "easy" then
  637. str = fullvoceasy(str, "sukun")
  638. elseif rules == "easynosukun" then
  639. str = fullvoceasy(str, "nosukun")
  640. elseif rules == "dflt" or rules == "idgham" then
  641. str = fullvoc(str, rules)
  642. else end
  643. str = unprotectarb(str)
  644. return str
  645. end
  646. function processnovoc(str, rules, scheme)
  647. str = takeoutarb(str)
  648. str = processarbnull(str, scheme)
  649. str = takeoutcapetc(str)
  650. str = protectarb(str)
  651. str = breakcmd(str)
  652. str = holdcmd(str)
  653. if scheme == "buckwalter" then
  654. str = processbuckw(str)
  655. else end
  656. if rules == "easy" or rules == "easynosukun" then
  657. str = novoceasy(str)
  658. elseif rules == "dflt" or rules == "idgham" then
  659. str = novoc(str)
  660. else end
  661. str = unprotectarb(str)
  662. return str
  663. end
  664. function processtrans(str, mode, rules, scheme)
  665. str = takeoutarb(str)
  666. str = processdiscretionary(str)
  667. str = processarbnull(str, scheme)
  668. str = takeoutabjad(str)
  669. str = protectarb(str)
  670. str = breakcmd(str)
  671. str = holdcmd(str)
  672. if scheme == "buckwalter" then
  673. str = processbuckw(str)
  674. end
  675. if mode == "dmg" then
  676. str = transdmg(str, rules)
  677. elseif mode == "loc" then
  678. str = transloc(str)
  679. elseif mode == "arabica" then
  680. str = transarabica(str)
  681. end
  682. str = unprotectarb(str)
  683. return str
  684. end
  685. function newarbmark(abbr, rtlmk, ltrmk)
  686. rtlmk = "\\arabicfont{}"..rtlmk
  687. table.insert(arbmarks, {a = abbr, b = rtlmk, c = ltrmk})
  688. table.sort(arbmarks, function(a ,b) return(#a.a > #b.a) end)
  689. return true
  690. end
  691. local function isintable(table, element)
  692. for i = 1,#table do
  693. if table[i].a == element then
  694. return true
  695. end
  696. end
  697. return false
  698. end
  699. function processarbmarks(str)
  700. if not isintable(arbmarks, str) then
  701. str = "\\LR{<??>}"..atletter.."\\al@wrong@mark{}"..atother
  702. else
  703. if tex.textdir == "TLT" then
  704. for i = 1,#arbmarks do
  705. str = string.gsub(str, arbmarks[i].a, arbmarks[i].c)
  706. end
  707. else
  708. for i = 1,#arbmarks do
  709. str = string.gsub(str, arbmarks[i].a, arbmarks[i].b)
  710. end
  711. end
  712. end
  713. return str
  714. end
  715. function uc(str)
  716. str = string.gsub(str, "(\\txtrans.?)(%b{})", function(tag, body)
  717. body = string.sub(body, 2, -2)
  718. return string.format("%s", body)
  719. end)
  720. -- Allah and ibn
  721. str = string.gsub(str, "(al%-lāh)([uai]?)", "{Allāh%2}")
  722. str = string.gsub(str, "([%'%-]?)(l%-lāh)([uai]?)", "%1{Llāh%3}")
  723. str = string.gsub(str, "(%s[%(%<%[]?)([i%']?b[n%.])", "%1{%2}")
  724. for i = 1,#lcuc do
  725. str = string.gsub(str, "^([%S]-%-[`']?)"..lcuc[i].a, "{%1"..lcuc[i].b.."}")
  726. end
  727. for i = 1,#lcuc do
  728. str = string.gsub(str, "(%s[%(%<%[]?)([%S]-%-[`']?)"..lcuc[i].a, "%1{%2"..lcuc[i].b.."}")
  729. end
  730. for i = 1,#lcuc do
  731. str = string.gsub(str, "^([%S]-%-ʿ)"..lcuc[i].a, "{%1"..lcuc[i].b.."}")
  732. end
  733. for i = 1,#lcuc do
  734. str = string.gsub(str, "(%s[%(%<%[]?)([%S]-%-ʿ)"..lcuc[i].a, "%1{%2"..lcuc[i].b.."}")
  735. end
  736. for i = 1,#lcuc do
  737. str = string.gsub(str, "^([%S]-%-ʾ)"..lcuc[i].a, "{%1"..lcuc[i].b.."}")
  738. end
  739. for i = 1,#lcuc do
  740. str = string.gsub(str, "(%s[%(%<%[]?)([%S]-%-ʾ)"..lcuc[i].a, "%1{%2"..lcuc[i].b.."}")
  741. end
  742. for i = 1,#lcuc do
  743. str = string.gsub(str, "^([`'])"..lcuc[i].a, "{%1"..lcuc[i].b.."}")
  744. end
  745. for i = 1,#lcuc do
  746. str = string.gsub(str, "(%s[%(%<%[]?)([`'])"..lcuc[i].a, "%1{%2"..lcuc[i].b.."}")
  747. end
  748. for i = 1,#lcuc do
  749. str = string.gsub(str, "^(ʾ)"..lcuc[i].a, "{%1"..lcuc[i].b.."}")
  750. end
  751. for i = 1,#lcuc do
  752. str = string.gsub(str, "(%s[%(%<%[]?)(ʾ)"..lcuc[i].a, "%1{%2"..lcuc[i].b.."}")
  753. end
  754. for i = 1,#lcuc do
  755. str = string.gsub(str, "^(ʿ)"..lcuc[i].a, "{%1"..lcuc[i].b.."}")
  756. end
  757. for i = 1,#lcuc do
  758. str = string.gsub(str, "(%s[%(%<%[]?)(ʿ)"..lcuc[i].a, "%1{%2"..lcuc[i].b.."}")
  759. end
  760. for i = 1,#lcuc do
  761. str = string.gsub(str, "^"..lcuc[i].a, lcuc[i].b)
  762. end
  763. for i = 1,#lcuc do
  764. str = string.gsub(str, "(%s[%(%<%[]?)"..lcuc[i].a, "%1"..lcuc[i].b)
  765. end
  766. return "\\txtrans{"..str.."}"
  767. end
  768. -- this function is adapted from an 'obsolete project' of Khaled
  769. -- Hosny's that dates back to 2010. Thanks to him.
  770. -- See https://github.com/khaledhosny/lualatex-arabic
  771. function abjadify(n)
  772. local abjadnum = ""
  773. n = tonumber(n)
  774. if n >= 1000 then
  775. for i=1,math.floor(n/1000) do
  776. abjadnum = abjadnum .. abjad[4][1]
  777. end
  778. n = math.fmod(n,1000)
  779. end
  780. if n >= 100 then
  781. abjadnum = abjadnum .. abjad[3][math.floor(n/100)]
  782. n = math.fmod(n, 100)
  783. end
  784. if n >= 10 then
  785. abjadnum = abjadnum .. abjad[2][math.floor(n/10)]
  786. n = math.fmod(n, 10)
  787. end
  788. if n >= 1 then
  789. abjadnum = abjadnum .. abjad[1][math.floor(n/1)]
  790. end
  791. return "\\arb[novoc]{"..abjadnum.."}"
  792. end
  793. function abraces(str)
  794. if tex.textdir == "TRT" then
  795. str = "\\}"..str.."\\{"
  796. elseif tex.textdir == "TLT" then
  797. str = "\\{"..str.."\\}"
  798. end
  799. return str
  800. end
  801. function aemph(str, opt)
  802. if tex.textdir == "TRT" then
  803. str = "$\\overline{\\text{\\textdir TRT{}"..str.."}}$"
  804. elseif tex.textdir == "TLT" then
  805. if opt == "over" then
  806. str = "$\\overline{\\text{"..str.."}}$"
  807. else
  808. str = "$\\underline{\\text{"..str.."}}$"
  809. end
  810. end
  811. return str
  812. end