Since this problem is related to XeTeX, I assume that your document can be migrated to LuaTeX without too much trouble. First of all, I have to say that I know nothing about Greek, but at least I understand a little bit about what needs to be done based on the files shared by Davislor in the comments. The solution I come up with is shown below.
- For now,
\textgreek
only supports text, which means almost no LaTeX commands will work correctly inside.
- I only defined the character mappings for
L2G_VARIANT_GRAVE
, L2G_VARIANT_ROUGH
and L2G_VARIANT_ACUTE
. You have to fill in new mappings inside _l2g_variant_indicator
table.
\documentclass{exam}
\usepackage{fontspec}
\usepackage{luacode}
\usepackage{expl3}
\setmainfont{DejaVu Serif}
\begin{luacode*}
L2G_VARIANT_ACUTE = 0x00000001
L2G_VARIANT_CIRCUM = 0x00000002
L2G_VARIANT_DIA = 0x00000004
L2G_VARIANT_DIACIRCUM = 0x00000008
L2G_VARIANT_DIAGRAVE = 0x00000010
L2G_VARIANT_GRAVE = 0x00000020
L2G_VARIANT_IOTASUB = 0x00000040
L2G_VARIANT_ROUGH = 0x00000080
L2G_VARIANT_SMOOTH = 0x00000100
l2g_variants = {
"acute", "circum", "dia", "diacircum", "diagrave", "grave", "iotasub", "rough", "smooth"
}
l2g_char_mapping = {
[97]=945,[98]=946,[99]=958,[100]=948,[101]=949,[102]=966,
[103]=947,[104]=951,[105]=953,[107]=954,[108]=955,[109]=956,
[110]=957,[111]=959,[112]=960,[113]=952,[114]=961,[116]=964,
[117]=965,[118]=989,[119]=969,[120]=958,[121]=968,[122]=950,
[65]=913,[66]=914,[67]=926,[68]=916,[69]=917,[70]=934,
[71]=915,[72]=919,[73]=921,[75]=922,[76]=923,[77]=924,
[78]=925,[79]=927,[80]=928,[81]=920,[82]=929,[84]=932,
[85]=933,[86]=988,[87]=937,[88]=926,[89]=936,[90]=918
}
_l2g_variant_indicator = {
["`"] = L2G_VARIANT_GRAVE,
["<"] = L2G_VARIANT_ROUGH,
["'"] = L2G_VARIANT_ACUTE
}
-- an integer version of _l2g_variant_indicator will be generated right away
l2g_variant_indicator = {}
for key, val in pairs(_l2g_variant_indicator) do
local ind = key:byte(1)
l2g_variant_indicator[ind] = val
end
l2g_variant_mapping = {
[97]={[256]=7936,[288]=7938,[352]=8066,[128]=7937,[160]=7939,[224]=8067,[257]=7940,[321]=8068,[129]=7941,[193]=8069,[258]=7942,[322]=8070,[130]=7943,[194]=8071,[320]=8064,[192]=8065,[32]=8048,[96]=8114,[1]=940,[65]=8116,[2]=8118,[66]=8119,[64]=8115},
[65]={[256]=7944,[288]=7946,[352]=8074,[128]=7945,[160]=7947,[224]=8075,[257]=7948,[321]=8076,[129]=7949,[193]=8077,[258]=7950,[322]=8078,[130]=7951,[194]=8079,[320]=8072,[192]=8073,[1]=902,[32]=8122,[64]=8124},
[104]={[256]=7968,[288]=7970,[352]=8082,[128]=7969,[160]=7971,[224]=8083,[257]=7972,[321]=8084,[129]=7973,[193]=8085,[258]=7974,[322]=8086,[320]=8080,[192]=8081,[1]=942,[65]=8132,[2]=8134,[66]=8135,[32]=8052,[64]=8131},
[72]={[256]=7976,[288]=7978,[352]=8090,[128]=7977,[160]=7979,[224]=8091,[257]=7980,[321]=8092,[129]=7981,[193]=8093,[258]=7982,[322]=8094,[320]=8088,[192]=8089,[1]=905,[32]=8138,[64]=8140},
[119]={[256]=8032,[288]=8034,[352]=8098,[128]=8097,[160]=8035,[224]=8099,[257]=8036,[321]=8100,[129]=8037,[193]=8101,[258]=8038,[322]=8102,[130]=8039,[194]=8103,[320]=8096,[192]=8097,[32]=8060,[96]=8178,[1]=974,[65]=8180,[2]=8182,[66]=8183,[64]=8179},
[87]={[256]=8040,[288]=8042,[352]=8106,[128]=8041,[160]=8043,[224]=8107,[257]=8044,[321]=8108,[129]=8045,[193]=8109,[258]=8046,[322]=8110,[130]=8047,[194]=8111,[320]=8104,[192]=8105,[1]=911,[32]=8186,[64]=8188},
[105]={[4]=970,[5]=912,[256]=7984,[288]=7986,[128]=7985,[160]=7987,[257]=7988,[129]=7989,[258]=7990,[130]=7991,[36]=8146,[16]=8146,[6]=8151,[8]=8151,[1]=943,[32]=8054,[2]=8150},
[117]={[4]=971,[5]=944,[256]=8016,[288]=8018,[128]=8017,[160]=8019,[257]=8020,[129]=8021,[258]=8022,[130]=8023,[36]=8162,[16]=8162,[6]=8167,[8]=8167,[1]=973,[32]=8058,[2]=8166},
[101]={[256]=7952,[288]=7954,[128]=7953,[160]=7955,[257]=7956,[129]=7957,[1]=941,[32]=8050},
[69]={[256]=7960,[288]=7962,[128]=7961,[160]=7963,[257]=7964,[129]=7965,[1]=904,[32]=8136},
[73]={[256]=7992,[288]=7994,[128]=7993,[160]=7995,[257]=7996,[129]=7997,[258]=7998,[130]=7999,[1]=906,[4]=938,[32]=8154},
[111]={[256]=8000,[288]=8002,[128]=8001,[160]=8003,[257]=8004,[129]=8005,[1]=972,[32]=8056},
[79]={[256]=8008,[288]=8010,[128]=8009,[160]=8011,[257]=8012,[129]=8013,[1]=908,[32]=8184},
[85]={[128]=8017,[160]=8027,[129]=8029,[130]=8031,[1]=910,[4]=939,[32]=8170},
[114]={[256]=8164,[128]=8165},
[82]={[128]=8172}
}
-- assume Lua 5.3 is used, otherwise may need to use bit32 library
-- https://stackoverflow.com/questions/5977654/how-do-i-use-the-bitwise-operator-xor-in-lua
function l2g_bitwise_or(a, b)
return a | b
end
function l2g_print_warning(s)
tex.print(string.format([[\PackageWarning{l2g}{%s}]], s))
end
function l2g_code_to_char(c)
return utf8.char(c)
end
l2gstate = {
["variant_flag"] = 0,
["variant_storage"] = -1,
["result"] = -1
}
function l2gstate:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
self.result = {}
self.variant_storage = {}
return obj
end
function l2gstate:get_last_variant()
local len = #self.variant_storage
if len == 0 then
return -1
end
local last_char = self.variant_storage[len]
return utf8.codepoint(last_char)
end
function l2gstate:register_variant(var_ccode)
local var_flag = l2g_variant_indicator[var_ccode]
self.variant_flag = l2g_bitwise_or(self.variant_flag, var_flag)
table.insert(self.variant_storage, l2g_code_to_char(var_ccode))
end
function l2gstate:discard_variant()
self.variant_flag = 0
self.variant_storage = {}
end
function l2gstate:add_variant_char()
for _, val in ipairs(self.variant_storage) do
table.insert(self.result, val)
end
end
function l2gstate:add_char(code)
table.insert(self.result, l2g_code_to_char(code))
end
function l2g(s)
local pos = 1
local cur_byte = -1
local cur_char = nil
local variant_state = l2gstate:new()
local char_find = nil
local inner_find = nil
local inner_char_find = nil
while pos <= utf8.len(s) do
cur_byte = utf8.codepoint(s, pos)
cur_char = utf8.char(cur_byte)
variant_find = l2g_variant_indicator[cur_byte]
if variant_find ~= nil then
if cur_byte == variant_state:get_last_variant() then
-- escaping current sequence
variant_state:add_char(cur_byte)
variant_state:discard_variant()
else
variant_state:register_variant(cur_byte)
end
goto continue
end
char_find = l2g_char_mapping[cur_byte]
if char_find ~= nil then
-- try to resolve variants
if variant_state.variant_flag > 0 then
inner_find = l2g_variant_mapping[cur_byte]
if inner_find ~= nil then
inner_char_find = inner_find[variant_state.variant_flag]
if inner_char_find ~= nil then
variant_state:add_char(inner_char_find)
else
l2g_print_warning(string.format("letter '%s' does not have variant type %d", cur_char, variant_state.variant_flag))
variant_state:add_variant_char()
variant_state:add_char(cur_byte)
end
else
l2g_print_warning(string.format("letter '%s' does not have any variant", cur_char))
variant_state:add_variant_char()
variant_state:add_char(cur_byte)
end
variant_state:discard_variant()
else
-- no variant set for this character
variant_state:add_char(char_find)
end
else
-- unable to find this character in mapping
variant_state:add_variant_char()
variant_state:add_char(cur_byte)
variant_state:discard_variant()
end
::continue::
pos = pos + 1
end
if variant_state:get_last_variant() ~= -1 then
variant_state:add_variant_char()
variant_state:discard_variant()
end
return table.concat(variant_state.result, "")
end
\end{luacode*}
\newcommand{\textgreek}[1]{
\directlua{
local res = l2g("\luaescapestring{#1}")
tex.print(res)
}
}
\begin{document}
\par \textgreek{<Ellhnik`h gl'wssa}
\par \textgreek{<Ellhnik<`h gl<'wssa}
\par \textgreek{<<Ellhnik``h gl''wssa}
\end{document}
The result is:
Ἑλληνικὴ γλώssα
Ἑλληνικἣ γλὥssα
<Ελληνικ‘η γλ’ωssα