arabluatex.lua 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321
  1. --[[
  2. This file is part of the `arabluatex' package
  3. ArabLuaTeX -- Processing ArabTeX notation under LuaLaTeX
  4. Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023
  5. Robert Alessi <alessi@robertalessi.net>
  6. Permission to use, copy, modify, and distribute this software for any
  7. purpose with or without fee is hereby granted, provided that the above
  8. copyright notice and this permission notice appear in all copies.
  9. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. Please send error reports and suggestions for improvements to Robert
  17. Alessi <alessi@robertalessi.net>
  18. --]]
  19. arabluatex = {}
  20. require("arabluatex_voc")
  21. require("arabluatex_fullvoc")
  22. require("arabluatex_novoc")
  23. require("arabluatex_trans")
  24. -- lpeg equivalent for string.gsub()
  25. local function gsub(s, patt, repl)
  26. patt = lpeg.P(patt)
  27. patt = lpeg.Cs((patt / repl + 1)^0)
  28. return lpeg.match(patt, s)
  29. end
  30. -- makeatletter, makeatother
  31. local atletter = "\\makeatletter{}"
  32. local atother = "\\makeatother{}"
  33. -- some basic patterns:
  34. local ascii = lpeg.R("az", "AZ", "@@")
  35. local dblbkslash = lpeg.Cs("\\")
  36. local bsqbrackets = lpeg.Cs{ "[" * ((1 - lpeg.S"[]") + lpeg.V(1))^0 * "]" }
  37. local bcbraces = lpeg.Cs{ "{" * ((1 - lpeg.S"{}") + lpeg.V(1))^0 * "}" }
  38. local spce = lpeg.Cs(" ")
  39. local spcenc = lpeg.P(" ")
  40. local cmdstar = lpeg.Cs(spce * lpeg.P("*"))
  41. local bsqbracketsii = lpeg.Cs(bsqbrackets^-2)
  42. local bcbracesii = lpeg.Cs(bcbraces^-2)
  43. local cmd = lpeg.Cs(dblbkslash * ascii^1 * cmdstar^-1)
  44. local rawcmd = lpeg.Cs(dblbkslash * ascii^1)
  45. local aftercmd = lpeg.Cs(lpeg.S("*[{,.?;:'`\"") + dblbkslash)
  46. local cmdargs = lpeg.Cs(spce^-1 * bsqbracketsii * bcbracesii * bsqbrackets^-1)
  47. local cmdargsnobs = lpeg.Cs(spce^-1 * bcbracesii)
  48. local arbargs = lpeg.Cs(spce^-1 * bsqbrackets^-1 * bcbraces)
  49. local baytargs = lpeg.Cs(spce * bcbraces * bsqbrackets^-1 * bcbraces)
  50. local arind = lpeg.Cs(dblbkslash * lpeg.P("arind") * spce^-1 * bsqbracketsii)
  51. local function protectarb(str)
  52. -- \App[]{}{}
  53. str = string.gsub(str, "(\\App%s?)(%b{})(%b{})", "%1[]%2%3")
  54. str = gsub(str, lpeg.P("\\App") * spcenc^-1 * bsqbrackets * bcbraces * bcbraces,
  55. function(opt, lem, rdg)
  56. opt = string.sub(opt, 2, -2)
  57. lem = string.sub(lem, 2, -2)
  58. rdg = string.sub(rdg, 2, -2)
  59. if opt == ""
  60. then
  61. return string.format("\\app{%s%s}", lem, rdg)
  62. else
  63. return string.format("\\app[%s]{%s%s}", opt, lem, rdg)
  64. end
  65. end)
  66. str = string.gsub(str, "(\\arb%s?)(%[.-%])(%b{})", "\\al@brk{\\arb%2%3}")
  67. str = string.gsub(str, "(\\LR%s?)(%b{})", "\\@LR%2")
  68. str = string.gsub(str, "(\\RL%s?)(%b{})", "\\@RL%2")
  69. return str
  70. end
  71. local function unprotectarb(str)
  72. str = string.gsub(str, "(\\@arb)(%[.-%])(%b{})", "\\arb%2%3")
  73. str = string.gsub(str, "(\\@LR)(%b{})", "\\LR%2")
  74. str = string.gsub(str, "(\\@RL)(%b{})", "\\RL%2")
  75. str = gsub(str, lpeg.Cs("\\al@brk") * bcbraces, function(tag, body)
  76. body = string.sub(body, 2, -2)
  77. return string.format("%s", body)
  78. end)
  79. return str
  80. end
  81. -- the following is to be taken out of \arb{}
  82. local outofarb = {
  83. "LRfootnote",
  84. "RLfootnote",
  85. "edtext",
  86. "pstart",
  87. "pend"
  88. }
  89. -- commands the arguments of which must not be processed by arabluatex
  90. -- inside \arb{}. 'albrkcmds' is what is set by default. 'brkcmds'
  91. -- collects the commands set in the preamble with \MkArbBreak{}
  92. local albrkcmds = {
  93. "begin",
  94. "end",
  95. "LRmarginpar",
  96. "arbmark",
  97. "abjad",
  98. "ayah",
  99. "SetArbNumbers"
  100. }
  101. local brkcmds = {}
  102. function arabluatex.mkarbbreak(str, opt)
  103. str = str ..","
  104. str = string.gsub(str, "%s+", "")
  105. local fieldstart = 1
  106. if opt == "dflt" then
  107. repeat
  108. local nexti = string.find(str, "%,", fieldstart)
  109. table.insert(brkcmds, string.sub(str, fieldstart, nexti-1))
  110. fieldstart = nexti +1
  111. until fieldstart > string.len(str)
  112. return brkcmds
  113. elseif opt == "out" then
  114. repeat
  115. local nexti = string.find(str, "%,", fieldstart)
  116. table.insert(outofarb, string.sub(str, fieldstart, nexti-1))
  117. fieldstart = nexti +1
  118. until fieldstart > string.len(str)
  119. return outofarb
  120. end
  121. end
  122. local function breakcmd(str)
  123. -- \par
  124. str = gsub(str, dblbkslash * lpeg.Cs("par") * cmdargsnobs, "\\al@brk{%1%2%3}")
  125. -- process \item[], then \item[]
  126. str = string.gsub(str, "\\(item.?)(%b[])",
  127. function(tag, body)
  128. body = string.sub(body, 2, -2)
  129. return string.format("\\al@brk{\\item[\\arb{%s}] }", body)
  130. end)
  131. str = string.gsub(str, "(\\item)(%s+)", "%1{}%2")
  132. -- \textcolor
  133. str = string.gsub(str, "\\(textcolor%s?)(%b{})(%b{})",
  134. function(tag, bodycolor, bodytext)
  135. bodycolor = string.sub(bodycolor, 2, -2)
  136. bodytext = string.sub(bodytext, 2, -2)
  137. return string.format("\\al@brk{\\%s{%s}{\\arb{%s}}}", tag, bodycolor, bodytext)
  138. end)
  139. -- commands set by default in outofarb
  140. for i = 1,#outofarb do
  141. str = gsub(str, dblbkslash * lpeg.Cs(outofarb[i]) * cmdargs, "}%1%2%3\\arb{")
  142. end
  143. -- commands set by default in albrkcmds
  144. for i = 1,#albrkcmds do
  145. str = gsub(str, dblbkslash * lpeg.Cs(albrkcmds[i]) * cmdargs, "\\al@brk{%1%2%3}")
  146. end
  147. -- user commands (brkcmds)
  148. if next(brkcmds) == nil then
  149. -- nothing to do
  150. else
  151. for i = 1,#brkcmds do
  152. str = gsub(str, dblbkslash * lpeg.Cs(brkcmds[i]) * cmdargs, "\\al@brk{%1%2%3}")
  153. end
  154. end
  155. return str
  156. end
  157. local function holdcmd(str)
  158. str = gsub(str, lpeg.Cs("\\arb") * bcbraces, function(tag, body)
  159. body = string.sub(body, 2, -2)
  160. body = gsub(body, cmd * spcenc^-1 * bsqbracketsii * spcenc^-1 * bcbraces, function(btag, bopt, bbody)
  161. bbody = string.sub(bbody, 2, -2)
  162. if string.find(btag, "@") then
  163. return holdcmd(string.format("}%s%s{%s}\\arb{", btag, bopt, bbody))
  164. else
  165. return holdcmd(string.format("}%s%s{\\arb{%s}}\\arb{", btag, bopt, bbody))
  166. end
  167. end)
  168. return string.format("%s{%s}", tag, body)
  169. end)
  170. str = string.gsub(str, "\\arb{}", "")
  171. return str
  172. end
  173. local indorarbnum = "Indian"
  174. function arabluatex.setnums(opt)
  175. if opt == "Indian"
  176. then
  177. indorarbnum = "Indian"
  178. else
  179. indorarbnum = "Arabic"
  180. end
  181. end
  182. local function arbnum(str) -- not used, see below
  183. str = string.gsub(str, "([0-9%,%-%/]+)", function(num)
  184. return string.reverse(num)
  185. end)
  186. return str
  187. end
  188. local function indnum(str, dir)
  189. if dir == "ltr"
  190. then
  191. -- do nothing
  192. else
  193. str = string.gsub(str, "([0-9%,%-%/]+)", function(num)
  194. return string.reverse(num)
  195. end)
  196. end
  197. if indorarbnum == "Indian"
  198. then
  199. for i = 1,#numbers do
  200. str = string.gsub(str, numbers[i].a, numbers[i].b)
  201. end
  202. end
  203. return str
  204. end
  205. local function processdiscretionary(str)
  206. str = string.gsub(str, "\\%-", "\\-{}")
  207. return str
  208. end
  209. local function processarbnull(str, scheme)
  210. if scheme == "buckwalter" then
  211. str = string.gsub(str, "(\\arbnull.?)(%b{})", function(tag, body)
  212. body = string.sub(body, 2, -2)
  213. return string.format("P%sP", body)
  214. end)
  215. else
  216. str = string.gsub(str, "(\\arbnull.?)(%b{})", function(tag, body)
  217. body = string.sub(body, 2, -2)
  218. return string.format("O%sO", body)
  219. end)
  220. end
  221. return str
  222. end
  223. local function takeout_abjad_ayah(str)
  224. str = string.gsub(str, "(\\abjad.?)(%b{})", function(tag, body)
  225. body = string.sub(body, 2, -2)
  226. return string.format("%s", body)
  227. end)
  228. str = string.gsub(str, "(\\ayah.?)(%b{})", function(tag, body)
  229. body = string.sub(body, 2, -2)
  230. if tonumber(body) ~= nil and str.len(body) < 4 then
  231. return string.format("(%s)", body)
  232. else
  233. return "<??>"
  234. end
  235. end)
  236. return str
  237. end
  238. local function takeoutcapetc(str)
  239. str = string.gsub(str, "(\\arb.?%[trans%])(%b{})", function(tag, body)
  240. body = string.sub(body, 2, -2)
  241. body = string.gsub(body, "(\\uc%s?)(%b{})", "\\Uc%2")
  242. return string.format("%s{%s}", tag, body)
  243. end)
  244. str = string.gsub(str, "(\\arbup.?)(%b{})", function(tag, body)
  245. body = string.sub(body, 2, -2)
  246. return string.format("%s", body)
  247. end)
  248. str = string.gsub(str, "(\\uc%s?)(%b{})", function(tag, body)
  249. body = string.sub(body, 2, -2)
  250. return string.format("%s", body)
  251. end)
  252. str = string.gsub(str, "\\uc%s", "")
  253. str = string.gsub(str, "\\linebreak", "")
  254. str = string.gsub(str, "\\%-", "")
  255. return str
  256. end
  257. local function checkwrnested(str)
  258. for i = 1,#outofarb do
  259. str = gsub(str, dblbkslash * lpeg.Cs(lpeg.P("LR") + lpeg.P("RL")) * cmdargs,
  260. function(prefix, tag, body)
  261. body = string.sub(body, 2, -2)
  262. if string.find(body, "\\"..outofarb[i]) then
  263. return atletter.."\\al@wrong@nesting{}"..atother
  264. else
  265. -- nothing to do, so proceed.
  266. end
  267. end)
  268. end
  269. return str
  270. end
  271. local function takeoutarb(str)
  272. str = checkwrnested(str)
  273. for i = 1,#outofarb do
  274. str = gsub(str, dblbkslash * lpeg.Cs(outofarb[i]) * cmdargs,
  275. function(prefix, tag, body)
  276. body = gsub(body, lpeg.P("\\arb"), "\\@rb")
  277. return string.format("%s%s%s", prefix, tag, body)
  278. end)
  279. end
  280. str = string.gsub(str, "(\\arb%s?)(%b{})", function(tag, body)
  281. body = string.sub(body, 2, -2)
  282. return string.format("\\al@brk{%s{%s}}", tag, body)
  283. end)
  284. str = string.gsub(str, "\\@rb", "\\arb")
  285. str = "\\arb{"..str.."}"
  286. return str
  287. end
  288. -- I owe this function to M. Krüger and U. Fischer. Thank to them both.
  289. local function getrenderer()
  290. local a = nil
  291. if font.getfont(font.current()).specification
  292. then
  293. a=font.getfont(font.current()).specification.features.normal.mode
  294. end
  295. return a
  296. end
  297. local function voc(str, rules)
  298. str = string.gsub(str, "\\arb(%b{})", function(inside)
  299. inside = string.sub(inside, 2, -2)
  300. for i = 1,#hamza do
  301. inside = string.gsub(inside, hamza[i].a, hamza[i].b)
  302. end
  303. if rules == "idgham" then
  304. for i = 1,#tanwin do
  305. inside = string.gsub(inside, tanwin[i].a, tanwin[i].b)
  306. end
  307. else
  308. for i = 1,#tanwineasy do
  309. inside = string.gsub(inside, tanwineasy[i].a, tanwineasy[i].b)
  310. end
  311. end
  312. for i = 1,#trigraphs do
  313. inside = string.gsub(inside, trigraphs[i].a, trigraphs[i].b)
  314. end
  315. if rules == "idgham" then
  316. for i = 1,#idgham do
  317. inside = string.gsub(inside, idgham[i].a, idgham[i].b)
  318. end
  319. end
  320. for i = 1,#digraphs do
  321. inside = string.gsub(inside, digraphs[i].a, digraphs[i].b)
  322. end
  323. for i = 1,#single do
  324. inside = string.gsub(inside, single[i].a, single[i].b)
  325. end
  326. for i = 1,#longv do
  327. inside = string.gsub(inside, longv[i].a, longv[i].b)
  328. end
  329. for i = 1,#shortv do
  330. inside = string.gsub(inside, shortv[i].a, shortv[i].b)
  331. end
  332. if getrenderer() == "harf"
  333. then
  334. for i = 1,#punctuationhb do
  335. inside = string.gsub(inside, punctuationhb[i].a,
  336. punctuationhb[i].b)
  337. end
  338. else
  339. for i = 1,#punctuation do
  340. inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
  341. end
  342. end
  343. for i = 1,#null do
  344. inside = string.gsub(inside, null[i].a, null[i].b)
  345. end
  346. inside = indnum(inside)
  347. return string.format("\\arabicfont{}%s", inside)
  348. end)
  349. return str
  350. end
  351. local function voceasy(str)
  352. str = string.gsub(str, "\\arb(%b{})", function(inside)
  353. inside = string.sub(inside, 2, -2)
  354. for i = 1,#hamzaeasy do
  355. inside = string.gsub(inside, hamzaeasy[i].a, hamzaeasy[i].b)
  356. end
  357. for i = 1,#tanwineasy do
  358. inside = string.gsub(inside, tanwineasy[i].a, tanwineasy[i].b)
  359. end
  360. for i = 1,#trigraphseasy do
  361. inside = string.gsub(inside, trigraphseasy[i].a, trigraphseasy[i].b)
  362. end
  363. for i = 1,#digraphs do
  364. inside = string.gsub(inside, digraphs[i].a, digraphs[i].b)
  365. end
  366. for i = 1,#single do
  367. inside = string.gsub(inside, single[i].a, single[i].b)
  368. end
  369. for i = 1,#longv do
  370. inside = string.gsub(inside, longv[i].a, longv[i].b)
  371. end
  372. for i = 1,#shortv do
  373. inside = string.gsub(inside, shortv[i].a, shortv[i].b)
  374. end
  375. if getrenderer() == "harf"
  376. then
  377. for i = 1,#punctuationhb do
  378. inside = string.gsub(inside, punctuationhb[i].a,
  379. punctuationhb[i].b)
  380. end
  381. else
  382. for i = 1,#punctuation do
  383. inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
  384. end
  385. end
  386. for i = 1,#null do
  387. inside = string.gsub(inside, null[i].a, null[i].b)
  388. end
  389. inside = indnum(inside)
  390. return string.format("\\arabicfont{}%s", inside)
  391. end)
  392. return str
  393. end
  394. local function fullvoc(str, rules)
  395. str = string.gsub(str, "\\arb(%b{})", function(inside)
  396. inside = string.sub(inside, 2, -2)
  397. for i = 1,#hamzafv do
  398. inside = string.gsub(inside, hamzafv[i].a, hamzafv[i].b)
  399. end
  400. if rules == "idgham" then
  401. for i = 1,#tanwinfv do
  402. inside = string.gsub(inside, tanwinfv[i].a, tanwinfv[i].b)
  403. end
  404. else
  405. for i = 1,#tanwinfveasy do
  406. inside = string.gsub(inside, tanwinfveasy[i].a, tanwinfveasy[i].b)
  407. end
  408. end
  409. for i = 1,#trigraphsfv do
  410. inside = string.gsub(inside, trigraphsfv[i].a, trigraphsfv[i].b)
  411. end
  412. if rules == "idgham" then
  413. for i = 1,#idgham do
  414. inside = string.gsub(inside, idgham[i].a, idgham[i].b)
  415. end
  416. end
  417. if rules == "idgham" then
  418. for i = 1,#digraphsfvidgham do
  419. inside = string.gsub(inside, digraphsfvidgham[i].a, digraphsfvidgham[i].b)
  420. end
  421. else
  422. for i = 1,#digraphsfv do
  423. inside = string.gsub(inside, digraphsfv[i].a, digraphsfv[i].b)
  424. end
  425. end
  426. for i = 1,#singlefv do
  427. inside = string.gsub(inside, singlefv[i].a, singlefv[i].b)
  428. end
  429. for i = 1,#longv do
  430. inside = string.gsub(inside, longv[i].a, longv[i].b)
  431. end
  432. for i = 1,#shortv do
  433. inside = string.gsub(inside, shortv[i].a, shortv[i].b)
  434. end
  435. if getrenderer() == "harf"
  436. then
  437. for i = 1,#punctuationhb do
  438. inside = string.gsub(inside, punctuationhb[i].a,
  439. punctuationhb[i].b)
  440. end
  441. else
  442. for i = 1,#punctuation do
  443. inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
  444. end
  445. end
  446. for i = 1,#null do
  447. inside = string.gsub(inside, null[i].a, null[i].b)
  448. end
  449. inside = indnum(inside)
  450. return string.format("\\arabicfont{}%s", inside)
  451. end)
  452. return str
  453. end
  454. local function fullvoceasy(str, rules)
  455. str = string.gsub(str, "\\arb(%b{})", function(inside)
  456. inside = string.sub(inside, 2, -2)
  457. for i = 1,#hamzafveasy do
  458. inside = string.gsub(inside, hamzafveasy[i].a, hamzafveasy[i].b)
  459. end
  460. for i = 1,#tanwinfveasy do
  461. inside = string.gsub(inside, tanwinfveasy[i].a, tanwinfveasy[i].b)
  462. end
  463. for i = 1,#trigraphsfveasy do
  464. inside = string.gsub(inside, trigraphsfveasy[i].a, trigraphsfveasy[i].b)
  465. end
  466. if rules == "nosukun" then
  467. for i = 1,#digraphsfveasy do
  468. inside = string.gsub(inside, digraphsfveasy[i].a, digraphsfveasy[i].b)
  469. end
  470. else
  471. for i = 1,#digraphsfv do
  472. inside = string.gsub(inside, digraphsfv[i].a, digraphsfv[i].b)
  473. end
  474. end
  475. if rules == "nosukun" then
  476. for i = 1,#singlefveasy do
  477. inside = string.gsub(inside, singlefveasy[i].a, singlefveasy[i].b)
  478. end
  479. else
  480. for i = 1,#singlefv do
  481. inside = string.gsub(inside, singlefv[i].a, singlefv[i].b)
  482. end
  483. end
  484. for i = 1,#longv do
  485. inside = string.gsub(inside, longv[i].a, longv[i].b)
  486. end
  487. for i = 1,#shortv do
  488. inside = string.gsub(inside, shortv[i].a, shortv[i].b)
  489. end
  490. if getrenderer() == "harf"
  491. then
  492. for i = 1,#punctuationhb do
  493. inside = string.gsub(inside, punctuationhb[i].a,
  494. punctuationhb[i].b)
  495. end
  496. else
  497. for i = 1,#punctuation do
  498. inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
  499. end
  500. end
  501. for i = 1,#null do
  502. inside = string.gsub(inside, null[i].a, null[i].b)
  503. end
  504. inside = indnum(inside)
  505. return string.format("\\arabicfont{}%s", inside)
  506. end)
  507. return str
  508. end
  509. local function novoc(str)
  510. str = string.gsub(str, "\\arb(%b{})", function(inside)
  511. inside = string.sub(inside, 2, -2)
  512. for i = 1,#hamza do
  513. inside = string.gsub(inside, hamza[i].a, hamza[i].b)
  514. end
  515. for i = 1,#tanwinnv do
  516. inside = string.gsub(inside, tanwinnv[i].a, tanwinnv[i].b)
  517. end
  518. for i = 1,#trigraphsnv do
  519. inside = string.gsub(inside, trigraphsnv[i].a, trigraphsnv[i].b)
  520. end
  521. for i = 1,#digraphs do
  522. inside = string.gsub(inside, digraphs[i].a, digraphs[i].b)
  523. end
  524. for i = 1,#single do
  525. inside = string.gsub(inside, single[i].a, single[i].b)
  526. end
  527. for i = 1,#longvnv do
  528. inside = string.gsub(inside, longvnv[i].a, longvnv[i].b)
  529. end
  530. for i = 1,#shortvnv do
  531. inside = string.gsub(inside, shortvnv[i].a, shortvnv[i].b)
  532. end
  533. if getrenderer() == "harf"
  534. then
  535. for i = 1,#punctuationhb do
  536. inside = string.gsub(inside, punctuationhb[i].a,
  537. punctuationhb[i].b)
  538. end
  539. else
  540. for i = 1,#punctuation do
  541. inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
  542. end
  543. end
  544. for i = 1,#null do
  545. inside = string.gsub(inside, null[i].a, null[i].b)
  546. end
  547. inside = indnum(inside)
  548. return string.format("\\arabicfont{}%s", inside)
  549. end)
  550. return str
  551. end
  552. local function novoceasy(str)
  553. str = string.gsub(str, "\\arb(%b{})", function(inside)
  554. inside = string.sub(inside, 2, -2)
  555. for i = 1,#hamzaeasy do
  556. inside = string.gsub(inside, hamzaeasy[i].a, hamzaeasy[i].b)
  557. end
  558. for i = 1,#tanwinnv do
  559. inside = string.gsub(inside, tanwinnv[i].a, tanwinnv[i].b)
  560. end
  561. for i = 1,#trigraphsnv do
  562. inside = string.gsub(inside, trigraphsnv[i].a, trigraphsnv[i].b)
  563. end
  564. for i = 1,#digraphs do
  565. inside = string.gsub(inside, digraphs[i].a, digraphs[i].b)
  566. end
  567. for i = 1,#single do
  568. inside = string.gsub(inside, single[i].a, single[i].b)
  569. end
  570. for i = 1,#longvnv do
  571. inside = string.gsub(inside, longvnv[i].a, longvnv[i].b)
  572. end
  573. for i = 1,#shortvnv do
  574. inside = string.gsub(inside, shortvnv[i].a, shortvnv[i].b)
  575. end
  576. if getrenderer() == "harf"
  577. then
  578. for i = 1,#punctuationhb do
  579. inside = string.gsub(inside, punctuationhb[i].a,
  580. punctuationhb[i].b)
  581. end
  582. else
  583. for i = 1,#punctuation do
  584. inside = string.gsub(inside, punctuation[i].a, punctuation[i].b)
  585. end
  586. end
  587. for i = 1,#null do
  588. inside = string.gsub(inside, null[i].a, null[i].b)
  589. end
  590. inside = indnum(inside)
  591. return string.format("\\arabicfont{}%s", inside)
  592. end)
  593. return str
  594. end
  595. local function transdmg(str, mode, rules)
  596. str = string.gsub(str, "\\arb(%b{})", function(inside)
  597. inside = string.sub(inside, 2, -2)
  598. if mode == "dmg"
  599. then
  600. for i = 1,#hamzatrnoinitialdmg do
  601. inside = string.gsub(inside, hamzatrnoinitialdmg[i].a, hamzatrnoinitialdmg[i].b)
  602. end
  603. elseif mode == "dmg+"
  604. then
  605. for i = 1,#hamzatrdmg do
  606. inside = string.gsub(inside, hamzatrdmg[i].a, hamzatrdmg[i].b)
  607. end
  608. end
  609. for i = 1,#tanwintrdmg do
  610. inside = string.gsub(inside, tanwintrdmg[i].a, tanwintrdmg[i].b)
  611. end
  612. for i = 1,#trigraphstrdmg do
  613. inside = string.gsub(inside, trigraphstrdmg[i].a, trigraphstrdmg[i].b)
  614. end
  615. if rules == "idgham" then
  616. for i = 1,#idghamtrdmg do
  617. inside = string.gsub(inside, idghamtrdmg[i].a, idghamtrdmg[i].b)
  618. end
  619. end
  620. for i = 1,#digraphstrdmg do
  621. inside = string.gsub(inside, digraphstrdmg[i].a, digraphstrdmg[i].b)
  622. end
  623. for i = 1,#singletrdmg do
  624. inside = string.gsub(inside, singletrdmg[i].a, singletrdmg[i].b)
  625. end
  626. for i = 1,#longvtrdmg do
  627. inside = string.gsub(inside, longvtrdmg[i].a, longvtrdmg[i].b)
  628. end
  629. for i = 1,#shortvtrdmg do
  630. inside = string.gsub(inside, shortvtrdmg[i].a, shortvtrdmg[i].b)
  631. end
  632. for i = 1,#punctuationtr do
  633. inside = string.gsub(inside, punctuationtr[i].a, punctuationtr[i].b)
  634. end
  635. for i = 1,#nulltr do
  636. inside = string.gsub(inside, nulltr[i].a, nulltr[i].b)
  637. end
  638. return string.format("\\altrfont{}%s", inside)
  639. end)
  640. return str
  641. end
  642. local function transloc(str)
  643. str = string.gsub(str, "\\arb(%b{})", function(inside)
  644. inside = string.sub(inside, 2, -2)
  645. for i = 1,#hamzatrloc do
  646. inside = string.gsub(inside, hamzatrloc[i].a, hamzatrloc[i].b)
  647. end
  648. for i = 1,#tanwintrloc do
  649. inside = string.gsub(inside, tanwintrloc[i].a, tanwintrloc[i].b)
  650. end
  651. for i = 1,#trigraphstrloc do
  652. inside = string.gsub(inside, trigraphstrloc[i].a, trigraphstrloc[i].b)
  653. end
  654. for i = 1,#digraphstrloc do
  655. inside = string.gsub(inside, digraphstrloc[i].a, digraphstrloc[i].b)
  656. end
  657. for i = 1,#singletrloc do
  658. inside = string.gsub(inside, singletrloc[i].a, singletrloc[i].b)
  659. end
  660. for i = 1,#longvtrloc do
  661. inside = string.gsub(inside, longvtrloc[i].a, longvtrloc[i].b)
  662. end
  663. for i = 1,#shortvtrloc do
  664. inside = string.gsub(inside, shortvtrloc[i].a, shortvtrloc[i].b)
  665. end
  666. for i = 1,#finaltrloc do
  667. inside = string.gsub(inside, finaltrloc[i].a, finaltrloc[i].b)
  668. end
  669. for i = 1,#punctuationtr do
  670. inside = string.gsub(inside, punctuationtr[i].a, punctuationtr[i].b)
  671. end
  672. for i = 1,#nulltr do
  673. inside = string.gsub(inside, nulltr[i].a, nulltr[i].b)
  674. end
  675. return string.format("\\altrfont{}%s", inside)
  676. end)
  677. return str
  678. end
  679. local function transarabica(str)
  680. str = string.gsub(str, "\\arb(%b{})", function(inside)
  681. inside = string.sub(inside, 2, -2)
  682. for i = 1,#hamzatrarabica do
  683. inside = string.gsub(inside, hamzatrarabica[i].a, hamzatrarabica[i].b)
  684. end
  685. for i = 1,#tanwintrloc do
  686. inside = string.gsub(inside, tanwintrloc[i].a, tanwintrloc[i].b)
  687. end
  688. for i = 1,#trigraphstrarabica do
  689. inside = string.gsub(inside, trigraphstrarabica[i].a, trigraphstrarabica[i].b)
  690. end
  691. for i = 1,#digraphstrarabica do
  692. inside = string.gsub(inside, digraphstrarabica[i].a, digraphstrarabica[i].b)
  693. end
  694. for i = 1,#singletrarabica do
  695. inside = string.gsub(inside, singletrarabica[i].a, singletrarabica[i].b)
  696. end
  697. for i = 1,#longvtrarabica do
  698. inside = string.gsub(inside, longvtrarabica[i].a, longvtrarabica[i].b)
  699. end
  700. for i = 1,#shortvtrloc do
  701. inside = string.gsub(inside, shortvtrloc[i].a, shortvtrloc[i].b)
  702. end
  703. for i = 1,#punctuationtr do
  704. inside = string.gsub(inside, punctuationtr[i].a, punctuationtr[i].b)
  705. end
  706. for i = 1,#nulltr do
  707. inside = string.gsub(inside, nulltr[i].a, nulltr[i].b)
  708. end
  709. return string.format("\\altrfont{}%s", inside)
  710. end)
  711. return str
  712. end
  713. local function processbuckw(str)
  714. str = string.gsub(str, "\\arb(%b{})", function(inside)
  715. inside = string.sub(inside, 2, -2)
  716. for i = 1,#buckwalter do
  717. inside = string.gsub(inside, buckwalter[i].a, buckwalter[i].b)
  718. end
  719. return string.format("\\arb{%s}", inside)
  720. end)
  721. return str
  722. end
  723. local function processarind(str, mode)
  724. str = gsub(str, arind * bcbraces, function(tag, arg)
  725. arg = string.sub(arg, 2, -2)
  726. if mode == "trans" then
  727. return string.format("%s{%s@\\txtrans{%s}}", tag, arg, arg)
  728. else
  729. arg = novoc(arg)
  730. arg = string.gsub(arg, "\\arabicfont%s?{}", "")
  731. return string.format("%s{%s@\\txarb{%s}}", tag, arg, arg)
  732. end
  733. end)
  734. return str
  735. end
  736. -- The following functions produce a copy of the original .tex source
  737. -- file in which all arabtex strings are replaced with Unicode
  738. -- equivalents
  739. local utffilesuffix = "_out"
  740. local export_utf = "no"
  741. function arabluatex.utffilesuffix(str)
  742. utffilesuffix = str
  743. return true
  744. end
  745. function arabluatex.doexport(str)
  746. export_utf = str
  747. return true
  748. end
  749. function arabluatex.openstream()
  750. local f = io.open(tex.jobname..utffilesuffix.."_tmp.tex", "a+")
  751. local preamble = io.open(tex.jobname..".tex", "r")
  752. for line in preamble:lines() do
  753. f:write(line, "\n")
  754. if string.find(line, "^%s-\\begin%s?{document}") then
  755. break
  756. end
  757. end
  758. preamble:close()
  759. f:close()
  760. return true
  761. end
  762. local function processarbtoutf(str)
  763. if export_utf ~= "arabverse" then
  764. str = "\\begin{arabexport}"..str
  765. else end
  766. --[[ -- of no use, see above takeout_abjad_ayah()
  767. str = string.gsub(str, "(\\txtrans%s?)(%b{})", function(tag, body)
  768. body = string.sub(body, 2, -2)
  769. body = string.gsub(body, "(\\abjad%s?)(%b{})", function(btag, bbody)
  770. bbody = string.sub(bbody, 2, -2)
  771. return string.format("%s", bbody)
  772. end)
  773. body = string.gsub(body, "(\\ayah%s?)(%b{})", function(btag, bbody)
  774. bbody = string.sub(bbody, 2, -2)
  775. return string.format("(%s)", bbody)
  776. end)
  777. return string.format("%s{%s}", tag, body)
  778. end)
  779. --]]
  780. str = string.gsub(str, "(\\txarb%s?)(%b{})", function(tag, body)
  781. body = string.sub(body, 2, -2)
  782. body = string.gsub(body, "(\\abjad%s?)(%b{})", function(btag, bbody)
  783. bbody = string.sub(bbody, 2, -2)
  784. if tonumber(bbody) ~= nil then
  785. bbody = arabluatex.abjadify(bbody)
  786. return string.format("\\aoline*{\\arb[novoc]{%s}}", bbody)
  787. else
  788. return string.format("%s{%s}", btag, bbody)
  789. end
  790. end)
  791. body = string.gsub(body, "(\\arbmark%s?)(%b{})", function(btag, bbody)
  792. bbody = string.sub(bbody, 2, -2)
  793. return string.format("%s[rl]{%s}", btag, bbody)
  794. end)
  795. body = string.gsub(body, "(\\ayah%s?)(%b{})", function(btag, bbody)
  796. bbody = string.sub(bbody, 2, -2)
  797. return string.format("\\arb[novoc]{%s^^^^06dd}", bbody)
  798. end)
  799. return string.format("%s{%s}", tag, body)
  800. end)
  801. str = string.gsub(str, "(\\bayt)%s?(%+?)(%b{})(%b[])(%b{})", function(tag, plus, argi, argii, argiii)
  802. argi = string.sub(argi, 2, -2)
  803. argii = string.sub(argii, 2, -2)
  804. argiii = string.sub(argiii, 2, -2)
  805. return string.format("%s%s*{\\arb{%s}}[\\arb{%s}]{\\arb{%s}}", tag, argi, argii, argiii)
  806. end)
  807. str = string.gsub(str, "(\\bayt)%s?(%+?)(%b{})(%b{})", function(tag, plus, argi, argii)
  808. argi = string.sub(argi, 2, -2)
  809. argii = string.sub(argii, 2, -2)
  810. return string.format("%s%s*{\\arb{%s}}{\\arb{%s}}", tag, plus, argi, argii)
  811. end)
  812. str = string.gsub(str, "(\\prname)%s?(%b{})", function(tag, body)
  813. body = string.sub(body, 2, -2)
  814. if string.find(body, "\\uc%s?%b{}") then
  815. return string.format("%s*{%s}", tag, body)
  816. else
  817. return string.format("%s{\\arb[trans]{\\uc{%s}}}", tag, body)
  818. end
  819. end)
  820. str = string.gsub(str, "(\\begin%s?{arab})(%b[])", "\\bgroup\\arb%2{")
  821. str = string.gsub(str, "(\\begin%s?{arab})", "\\bgroup\\arb{")
  822. str = string.gsub(str, "\\end%s?{arab}", "}\\egroup")
  823. -- This does not work, while the following two do. Look into this later.
  824. -- str = gsub(str, lpeg.Cs("\\arb") * spcenc * bsqbrackets^-1 * bcbraces, function(tag, opt, body)
  825. -- body = string.sub(body, 2, -2)
  826. -- return string.format("%s%s\\@al@pr@ob%s\\@al@pr@cb", tag, opt, body)
  827. -- end)
  828. str = string.gsub(str, "(\\arb%s?)(%b[])(%b{})", function(tag, opt, body)
  829. body = string.sub(body, 2, -2)
  830. return string.format("%s%s\\@al@pr@ob%s\\@al@pr@cb", tag, opt, body)
  831. end)
  832. str = string.gsub(str, "(\\arb)%s?(%b{})", function(tag, body)
  833. body = string.sub(body, 2, -2)
  834. return string.format("%s\\@al@pr@ob%s\\@al@pr@cb", tag, body)
  835. end)
  836. str = string.gsub(str, "(\\arbmark)%s?(%b[])(%b{})", function(tag, opt, body)
  837. body = string.sub(body, 2, -2)
  838. return string.format("%s%s\\@al@pr@ob%s\\@al@pr@cb", tag, opt, body)
  839. end)
  840. str = string.gsub(str, "(\\arbmark)%s?(%b{})", function(tag, body)
  841. body = string.sub(body, 2, -2)
  842. return string.format("%s\\@al@pr@ob%s\\@al@pr@cb", tag, body)
  843. end)
  844. str = string.gsub(str, "(\\[Uu]c)%s?(%b{})", function(tag, body)
  845. body = string.sub(body, 2, -2)
  846. return string.format("%s\\@al@pr@ob%s\\@al@pr@cb", tag, body)
  847. end)
  848. str = string.gsub(str, "{", "\\@al@ob")
  849. str = string.gsub(str, "} ", "\\@al@cb@sp")
  850. str = string.gsub(str, "}", "\\@al@cb")
  851. str = string.gsub(str, "\\@al@pr@ob", "{")
  852. str = string.gsub(str, "\\@al@pr@cb", "}")
  853. str = string.gsub(str, "(%b{})", function(body)
  854. body = string.sub(body, 2, -2)
  855. body = string.gsub(body, "(%s?)(\\@al@ob)", "%1{")
  856. body = string.gsub(body, "(\\@al@cb@sp)", "} ")
  857. body = string.gsub(body, "(\\@al@cb)(%s?)", "}%2")
  858. return string.format("{%s}", body)
  859. end)
  860. if export_utf ~= "arabverse" then
  861. str = str.."\\end{arabexport}"
  862. else end
  863. return str
  864. end
  865. function arabluatex.arbtoutf(str)
  866. str = processarbtoutf(str)
  867. str = "\\ArbOutFile{"..str.."}"
  868. str = string.gsub(str, "(\\ArbOutFile)%s?(%b{})", function(tag, body)
  869. body = string.sub(body, 2, -2)
  870. body = gsub(body, lpeg.Cs("\\arb") * arbargs, "}%1%2\\ArbOutFile{")
  871. return string.format("%s{%s}", tag, body)
  872. end)
  873. str = string.gsub(str, "(\\ArbOutFile)%s?(%b{})", function(tag, body)
  874. body = string.sub(body, 2, -2)
  875. body = string.gsub(body, "(\\[Uu]c)%s?(%b{})", "}%1%2\\ArbOutFile{")
  876. return string.format("%s{%s}", tag, body)
  877. end)
  878. str = string.gsub(str, "(\\ArbOutFile)%s?(%b{})", function(tag, body)
  879. body = string.sub(body, 2, -2)
  880. body = gsub(body, lpeg.Cs("\\arbmark") * arbargs, "}%1%2\\ArbOutFile{")
  881. return string.format("%s{%s}", tag, body)
  882. end)
  883. return str
  884. end
  885. function arabluatex.tooutfile(str, nl)
  886. local f = io.open(tex.jobname..utffilesuffix.."_tmp.tex", "a+")
  887. if nl == "newline" then
  888. f:write(str, "\n\n")
  889. else
  890. f:write(str)
  891. end
  892. f:close()
  893. return str
  894. end
  895. function arabluatex.closestream()
  896. local f = io.open(tex.jobname..utffilesuffix.."_tmp.tex", "r")
  897. local o = io.open(tex.jobname..utffilesuffix..".tex", "w")
  898. local t = f:read("*a")
  899. t = string.gsub(t, "\\arabicfont{}", "")
  900. t = string.gsub(t, "\\altrfont{}", "")
  901. t = string.gsub(t, "\\par ", "\n\n")
  902. t = string.gsub(t, "(\\@al@ob)", "{")
  903. t = string.gsub(t, "(\\@al@cb@sp)", "} ")
  904. t = string.gsub(t, "(\\@al@cb)(%s?)", "}")
  905. t = string.gsub(t, "(\\bgroup%s?)(\\txarb%s?)(%b{})(\\egroup%s?)", function(tagio, tag, body, tagic)
  906. body = string.sub(body, 2, -2)
  907. return string.format("\\begin{txarab}%s\\end{txarab}", body)
  908. end)
  909. t = string.gsub(t, "(\\bgroup%s?)(\\txtrans%s?)(%b{})(\\egroup%s?)", function(tagio, tag, body, tagic)
  910. body = string.sub(body, 2, -2)
  911. return string.format("\\begin{txarabtr}%s\\end{txarabtr}", body)
  912. end)
  913. t = gsub(t, lpeg.Cs("\\begin") * spcenc^-1 * bcbraces * cmdargs,
  914. "\n%1%2%3\n")
  915. t = string.gsub(t, "(\\\\)(%s?)", "%1\n")
  916. t = string.gsub(t, "(\\\\)(\n)(\\end%s?)(%b{})", "%1%3%4")
  917. t = string.gsub(t, "%s-\n(\\begin%s?)(%b{})", "\n%1%2")
  918. t = string.gsub(t, "(\\item)", "\n%1")
  919. t = string.gsub(t, "\n\n(\\item)", "\n%1")
  920. t = string.gsub(t, "(\\end%s?)(%b{})", "%1%2\n")
  921. t = string.gsub(t, "([^\n]%s-)(\\end)%s?(%b{})", "%1\n%2%3")
  922. t = string.gsub(t, "\n\n\n", "\n\n")
  923. t = string.gsub(t, "(\\txarb%s?%{)(\\txarb%s?)(%b{})(%})",
  924. function(tagio, tagii, body, tagic)
  925. body = string.sub(body, 2, -2)
  926. return
  927. string.format("%s%s%s", tagio, body, tagic)
  928. end)
  929. t = string.gsub(t, "(\\prname%s?%*%{)(\\txtrans%s?)(%b{})(%})",
  930. function(tagio, tagii, body, tagic)
  931. body = string.sub(body, 2, -2)
  932. return string.format("%s%s%s", tagio, body, tagic)
  933. end)
  934. if string.find(t, "\\begin%s?{document}.-\\arb%s?[%[%{]")
  935. or
  936. string.find(t, "\\begin%s?{document}.-\\[Uu]c%s?%b{}")
  937. or
  938. string.find(t, "\\begin%s?{document}.-\\abjad%s?%b{}")
  939. or
  940. string.find(t, "\\begin%s?{document}.-\\ayah%s?%b{}")
  941. then
  942. -- issue a warning:
  943. tex.print([[\unexpanded{\PackageWarningNoLine{arabluatex}{]]
  944. ..
  945. [[There are still 'arabtex' strings to be converted. ]]
  946. ..
  947. [[Please open ]] .. tex.jobname .. utffilesuffix .. ".tex" ..
  948. [[ and compile it one more time}}]])
  949. --
  950. else end
  951. t = gsub(t, rawcmd * spce^1 * aftercmd, "%1%3")
  952. t = t.."\n\\end{document}"
  953. io.write(t)
  954. o:write(t)
  955. f:close()
  956. o:close()
  957. os.remove(tex.jobname..utffilesuffix.."_tmp.tex")
  958. return true
  959. end
  960. -- Process standard arabluatex modes:
  961. function arabluatex.processvoc(str, rules, scheme)
  962. str = takeoutarb(str)
  963. str = processarbnull(str, scheme)
  964. str = takeoutcapetc(str)
  965. str = protectarb(str)
  966. str = breakcmd(str)
  967. str = holdcmd(str)
  968. str = processarind(str)
  969. if scheme == "buckwalter" then
  970. str = processbuckw(str)
  971. else end
  972. if rules == "easy" or rules == "easynosukun" then
  973. str = voceasy(str)
  974. elseif rules == "dflt" or rules == "idgham" then
  975. str = voc(str, rules)
  976. else end
  977. str = unprotectarb(str)
  978. if export_utf == "yes" then
  979. tofile = "\\txarb{"..str.."}"
  980. arabluatex.tooutfile(tofile)
  981. elseif export_utf == "arabverse" then
  982. tofile = "\\txarb{"..str.."}"
  983. arabluatex.tooutfile(tofile)
  984. else
  985. return str
  986. end
  987. return ""
  988. end
  989. function arabluatex.processfullvoc(str, rules, scheme)
  990. str = takeoutarb(str)
  991. str = processarbnull(str, scheme)
  992. str = takeoutcapetc(str)
  993. str = protectarb(str)
  994. str = breakcmd(str)
  995. str = holdcmd(str)
  996. str = processarind(str)
  997. if scheme == "buckwalter" then
  998. str = processbuckw(str)
  999. else end
  1000. if rules == "easy" then
  1001. str = fullvoceasy(str, "sukun")
  1002. elseif rules == "easynosukun" then
  1003. str = fullvoceasy(str, "nosukun")
  1004. elseif rules == "dflt" or rules == "idgham" then
  1005. str = fullvoc(str, rules)
  1006. else end
  1007. str = unprotectarb(str)
  1008. if export_utf == "yes" then
  1009. tofile = "\\txarb{"..str.."}"
  1010. arabluatex.tooutfile(tofile)
  1011. elseif export_utf == "arabverse" then
  1012. tofile = "\\txarb{"..str.."}"
  1013. arabluatex.tooutfile(tofile)
  1014. else
  1015. return str
  1016. end
  1017. return ""
  1018. end
  1019. function arabluatex.processnovoc(str, rules, scheme)
  1020. str = takeoutarb(str)
  1021. str = processarbnull(str, scheme)
  1022. str = takeoutcapetc(str)
  1023. str = protectarb(str)
  1024. str = breakcmd(str)
  1025. str = holdcmd(str)
  1026. str = processarind(str)
  1027. if scheme == "buckwalter" then
  1028. str = processbuckw(str)
  1029. else end
  1030. if rules == "easy" or rules == "easynosukun" then
  1031. str = novoceasy(str)
  1032. elseif rules == "dflt" or rules == "idgham" then
  1033. str = novoc(str)
  1034. else end
  1035. str = unprotectarb(str)
  1036. if export_utf == "yes" then
  1037. tofile = "\\txarb{"..str.."}"
  1038. arabluatex.tooutfile(tofile)
  1039. elseif export_utf == "arabverse" then
  1040. tofile = "\\txarb{"..str.."}"
  1041. arabluatex.tooutfile(tofile)
  1042. else
  1043. return str
  1044. end
  1045. return ""
  1046. end
  1047. function arabluatex.processtrans(str, mode, rules, scheme)
  1048. str = takeoutarb(str)
  1049. str = processdiscretionary(str)
  1050. str = processarbnull(str, scheme)
  1051. str = takeout_abjad_ayah(str)
  1052. str = protectarb(str)
  1053. str = breakcmd(str)
  1054. str = holdcmd(str)
  1055. str = processarind(str, "trans")
  1056. if scheme == "buckwalter" then
  1057. str = processbuckw(str)
  1058. end
  1059. if mode == "dmg" or mode == "dmg+" then
  1060. str = transdmg(str, mode, rules)
  1061. elseif mode == "loc" then
  1062. str = transloc(str)
  1063. elseif mode == "arabica" then
  1064. str = transarabica(str)
  1065. end
  1066. str = unprotectarb(str)
  1067. if export_utf == "yes" then
  1068. tofile = "\\txtrans{"..str.."}"
  1069. arabluatex.tooutfile(tofile)
  1070. elseif export_utf == "arabverse" then
  1071. tofile = "\\txtrans{"..str.."}"
  1072. arabluatex.tooutfile(tofile)
  1073. else
  1074. return str
  1075. end
  1076. return ""
  1077. end
  1078. function arabluatex.newarbmark(abbr, rtlmk, ltrmk)
  1079. abbr = "@"..abbr
  1080. rtlmk = "\\arabicfont{}"..rtlmk
  1081. table.insert(arbmarks, {a = abbr, b = rtlmk, c = ltrmk})
  1082. table.sort(arbmarks, function(a ,b) return(#a.a > #b.a) end)
  1083. return true
  1084. end
  1085. local function isintable(table, element)
  1086. for i = 1,#table do
  1087. if table[i].a == element then
  1088. return true
  1089. end
  1090. end
  1091. return false
  1092. end
  1093. function arabluatex.processarbmarks(str, dir)
  1094. str = "@"..str
  1095. if not isintable(arbmarks, str) then
  1096. str = "\\LR{<??>}"..atletter.."\\al@wrong@mark{}"..atother
  1097. else
  1098. if dir == "lr" then
  1099. for i = 1,#arbmarks do
  1100. str = string.gsub(str, arbmarks[i].a, arbmarks[i].c)
  1101. end
  1102. elseif dir == "rl" then
  1103. for i = 1,#arbmarks do
  1104. str = string.gsub(str, arbmarks[i].a, arbmarks[i].b)
  1105. end
  1106. elseif tex.textdir == "TLT" then
  1107. for i = 1,#arbmarks do
  1108. str = string.gsub(str, arbmarks[i].a, arbmarks[i].c)
  1109. end
  1110. else
  1111. for i = 1,#arbmarks do
  1112. str = string.gsub(str, arbmarks[i].a, arbmarks[i].b)
  1113. end
  1114. end
  1115. end
  1116. if export_utf == "yes" then
  1117. tofile = str
  1118. arabluatex.tooutfile(tofile)
  1119. elseif export_utf == "arabverse" then
  1120. tofile = str
  1121. arabluatex.tooutfile(tofile)
  1122. else
  1123. return str
  1124. end
  1125. return ""
  1126. end
  1127. function arabluatex.uc(str)
  1128. str = string.gsub(str, "(\\txtrans.?)(%b{})", function(tag, body)
  1129. body = string.sub(body, 2, -2)
  1130. return string.format("%s", body)
  1131. end)
  1132. str = string.gsub(str, "\\altrfont%s?{}", "")
  1133. str = string.gsub(str, "{", "\\@al@ob")
  1134. str = string.gsub(str, "} ", "\\@al@cb@sp ")
  1135. str = string.gsub(str, "}", "\\@al@cb")
  1136. -- Allah and ibn
  1137. str = string.gsub(str, "(al%-lāh)([uai]?)", "{Allāh%2}")
  1138. str = string.gsub(str, "([%'%-]?)(l%-lāh)([uai]?)", "%1{Llāh%3}")
  1139. str = string.gsub(str, "(%s[%(%<%[]?)([i%']?b[n%.])", "%1{%2}")
  1140. for i = 1,#lcuc do
  1141. str = string.gsub(str, "^([%S]-%-[`']?)"..lcuc[i].a, "{%1"..lcuc[i].b.."}")
  1142. end
  1143. for i = 1,#lcuc do
  1144. str = string.gsub(str, "(%s[%(%<%[]?)([%S]-%-[`']?)"..lcuc[i].a, "%1{%2"..lcuc[i].b.."}")
  1145. end
  1146. for i = 1,#lcuc do
  1147. str = string.gsub(str, "^([%S]-%-ʿ)"..lcuc[i].a, "{%1"..lcuc[i].b.."}")
  1148. end
  1149. for i = 1,#lcuc do
  1150. str = string.gsub(str, "(%s[%(%<%[]?)([%S]-%-ʿ)"..lcuc[i].a, "%1{%2"..lcuc[i].b.."}")
  1151. end
  1152. for i = 1,#lcuc do
  1153. str = string.gsub(str, "^([%S]-%-ʾ)"..lcuc[i].a, "{%1"..lcuc[i].b.."}")
  1154. end
  1155. for i = 1,#lcuc do
  1156. str = string.gsub(str, "(%s[%(%<%[]?)([%S]-%-ʾ)"..lcuc[i].a, "%1{%2"..lcuc[i].b.."}")
  1157. end
  1158. for i = 1,#lcuc do
  1159. str = string.gsub(str, "^([`'])"..lcuc[i].a, "{%1"..lcuc[i].b.."}")
  1160. end
  1161. for i = 1,#lcuc do
  1162. str = string.gsub(str, "(%s[%(%<%[]?)([`'])"..lcuc[i].a, "%1{%2"..lcuc[i].b.."}")
  1163. end
  1164. for i = 1,#lcuc do
  1165. str = string.gsub(str, "^(ʾ)"..lcuc[i].a, "{%1"..lcuc[i].b.."}")
  1166. end
  1167. for i = 1,#lcuc do
  1168. str = string.gsub(str, "(%s[%(%<%[]?)(ʾ)"..lcuc[i].a, "%1{%2"..lcuc[i].b.."}")
  1169. end
  1170. for i = 1,#lcuc do
  1171. str = string.gsub(str, "^(ʿ)"..lcuc[i].a, "{%1"..lcuc[i].b.."}")
  1172. end
  1173. for i = 1,#lcuc do
  1174. str = string.gsub(str, "(%s[%(%<%[]?)(ʿ)"..lcuc[i].a, "%1{%2"..lcuc[i].b.."}")
  1175. end
  1176. for i = 1,#lcuc do
  1177. str = string.gsub(str, "^"..lcuc[i].a, lcuc[i].b)
  1178. end
  1179. for i = 1,#lcuc do
  1180. str = string.gsub(str, "(%s[%(%<%[]?)"..lcuc[i].a, "%1"..lcuc[i].b)
  1181. end
  1182. str = string.gsub(str, "{", "")
  1183. str = string.gsub(str, "}", "")
  1184. str = string.gsub(str, "\\@al@ob", "{")
  1185. str = string.gsub(str, "\\@al@cb@sp ", "} ")
  1186. str = string.gsub(str, "\\@al@cb", "}")
  1187. str = "\\altrfont{}"..str
  1188. if export_utf == "yes" then
  1189. tofile = str
  1190. arabluatex.tooutfile(tofile)
  1191. elseif export_utf == "arabverse" then
  1192. tofile = str
  1193. arabluatex.tooutfile(tofile)
  1194. else
  1195. return str
  1196. end
  1197. return ""
  1198. end
  1199. -- this function is adapted from an 'obsolete project' of Khaled
  1200. -- Hosny's that dates back to 2010. Thanks to him.
  1201. -- See https://github.com/khaledhosny/lualatex-arabic
  1202. function arabluatex.abjadify(n)
  1203. local abjadnum = ""
  1204. n = tonumber(n)
  1205. if n >= 1000 then
  1206. for i=1,math.floor(n/1000) do
  1207. abjadnum = abjadnum .. abjad[4][1]
  1208. end
  1209. n = math.fmod(n,1000)
  1210. end
  1211. if n >= 100 then
  1212. abjadnum = abjadnum .. abjad[3][math.floor(n/100)]
  1213. n = math.fmod(n, 100)
  1214. end
  1215. if n >= 10 then
  1216. abjadnum = abjadnum .. abjad[2][math.floor(n/10)]
  1217. n = math.fmod(n, 10)
  1218. end
  1219. if n >= 1 then
  1220. abjadnum = abjadnum .. abjad[1][math.floor(n/1)]
  1221. end
  1222. return "\\arb[novoc]{"..abjadnum.."}"
  1223. end
  1224. function arabluatex.abraces(str)
  1225. if tex.textdir == "TRT"
  1226. then
  1227. if getrenderer() == "harf"
  1228. then
  1229. str = "\\{"..str.."\\}"
  1230. else
  1231. str = "\\}"..str.."\\{"
  1232. end
  1233. elseif tex.textdir == "TLT" then
  1234. str = "\\{"..str.."\\}"
  1235. end
  1236. return str
  1237. end
  1238. function arabluatex.aemph(str, opt)
  1239. if tex.textdir == "TRT" then
  1240. str = "\\aoline{\\textdir TRT{}"..str.."}"
  1241. elseif tex.textdir == "TLT" then
  1242. if opt == "over" then
  1243. str = "\\aoline{"..str.."}"
  1244. else
  1245. str = "\\auline{"..str.."}"
  1246. end
  1247. end
  1248. return str
  1249. end
  1250. function arabluatex.ayah(str)
  1251. if tonumber(str) ~= nil and str.len(str) < 4 then
  1252. if tex.textdir == "TRT" then
  1253. -- end of ayah is typeset LTR and the number comes after the
  1254. -- sign (see https://github.com/aliftype/amiri/issues/263#issuecomment-1872979252)
  1255. str = "{\\textdir TLT ".."^^^^06dd"..indnum(str, "ltr").."}"
  1256. elseif tex.textdir == "TLT" then
  1257. str = "\\arb[trans]{("..str..")}"
  1258. end
  1259. return str
  1260. else
  1261. return "\\LR{<??>}"
  1262. end
  1263. end