summaryrefslogtreecommitdiff
path: root/macros/luatex/generic/luaotfload/luaotfload-case-el.lua
diff options
context:
space:
mode:
Diffstat (limited to 'macros/luatex/generic/luaotfload/luaotfload-case-el.lua')
-rw-r--r--macros/luatex/generic/luaotfload/luaotfload-case-el.lua294
1 files changed, 294 insertions, 0 deletions
diff --git a/macros/luatex/generic/luaotfload/luaotfload-case-el.lua b/macros/luatex/generic/luaotfload/luaotfload-case-el.lua
new file mode 100644
index 0000000000..1b329c4a65
--- /dev/null
+++ b/macros/luatex/generic/luaotfload/luaotfload-case-el.lua
@@ -0,0 +1,294 @@
+local unicode_data = require'luaotfload-unicode'
+
+local has_accent = 1
+local has_combining_dialytika = 2
+local has_ypogegrammeni = 4
+local has_other_diacritic = 8
+local has_vowel = 16
+local vowels = {
+ [0x03B1] = true,
+ [0x0391] = true,
+ [0x03B5] = true,
+ [0x0395] = true,
+ [0x03B7] = true,
+ [0x0397] = true,
+ [0x03B9] = true,
+ [0x0399] = true,
+ [0x03BF] = true,
+ [0x039F] = true,
+ [0x03C9] = true,
+ [0x03A9] = true,
+ [0x03C5] = true,
+ [0x03A5] = true,
+ [0x03F5] = true,
+}
+local diacritic_data = {
+ [0x0300] = has_accent,
+ [0x0301] = has_accent,
+ [0x0302] = has_accent,
+ [0x0303] = has_accent,
+ [0x0311] = has_accent,
+ [0x0342] = has_accent,
+ [0x0308] = has_combining_dialytika,
+ [0x0344] = has_accent | has_combining_dialytika,
+ [0x0345] = has_ypogegrammeni,
+ [0x0304] = has_other_diacritic,
+ [0x0306] = has_other_diacritic,
+ [0x0313] = has_other_diacritic,
+ [0x0314] = has_other_diacritic,
+ [0x0343] = has_other_diacritic,
+}
+
+local mapping_tables = unicode_data.casemapping
+local soft_dotted = unicode_data.soft_dotted
+local ccc = unicode_data.ccc
+
+local uppercase = mapping_tables.uppercase
+local lowercase = mapping_tables.lowercase
+local cased = mapping_tables.cased
+local case_ignorable = mapping_tables.case_ignorable
+
+local otfregister = fonts.constructors.features.otf.register
+
+local direct = node.direct
+local is_char = direct.is_char
+local has_glyph = direct.has_glyph
+local uses_font = direct.uses_font
+local getnext = direct.getnext
+local setchar = direct.setchar
+local setdisc = direct.setdisc
+local getdisc = direct.getdisc
+local getfield = direct.getfield
+local remove = direct.remove
+local copy = direct.copy
+local insert_after = direct.insert_after
+local traverse = direct.traverse
+
+local disc = node.id'disc'
+
+local nfd = {} -- FIXME: Data
+
+local data do
+ for cp = 1, 0xFF, 1 do -- FIXME: Correct limits
+ local decomposed = nfd[cp]
+ local first = decomposed and decomposed[1] or cp
+ local upper = uppercase[first]
+ local data = vowels[first] and has_vowel or 0
+ if decomposed then
+ for i=1, #decomposed do
+ data = data | (diacritic_data[decomposed[i]] or 0)
+ end
+ end
+ end
+end
+
+--[[ We make some implicit assumptions about contexts in SpecialCasing.txt here which happened to be true when I wrote the code:
+--
+-- * Before_Dot only appears as Not_Before_Dot
+-- * No other context appears with Not_
+-- * Final_Sigma is never language dependent
+-- * Other contexts are always language dependent
+-- * The only languages with special mappings are Lithuanian (lt/"LTH "/lit), Turkish (tr/"TRK "/tur), and Azeri/Azerbaijani (az/"AZE "/aze)
+]]
+
+local font_lang = setmetatable({}, {__index = function(t, fid)
+ local f = font.getfont(fid)
+ local lang = f.specification.features.normal.language
+ lang = lang == 'lth' and 'lt' or lang == 'trk' and 'tr' or lang == 'aze' and 'az' or false
+ t[fid] = lang
+ return lang
+end})
+
+local function is_Final_Sigma(font, mapping, n, after)
+ mapping = mapping.Final_Sigma
+ if not mapping then return false end
+ mapping = mapping._
+ if not mapping then return false end
+ n = getnext(n)
+ repeat
+ while n do
+ local char, id = is_char(n, font)
+ if id == disc then
+ after = getnext(n)
+ n = getfield(n, 'replace')
+ char, id = is_char(n, font)
+ elseif char then
+ if not case_ignorable[char] then
+ return not cased[char] and mapping
+ end
+ n = getnext(n)
+ else
+ return mapping
+ end
+ end
+ n, after = after
+ until not n
+ return mapping
+end
+
+local function is_More_Above(font, mapping, n, after)
+ mapping = mapping.More_Above
+ if not mapping then return false end
+ mapping = mapping._
+ if not mapping then return false end
+ n = getnext(n)
+ repeat
+ while n do
+ local char, id = is_char(n, font)
+ if id == disc then
+ after = getnext(n)
+ n = getfield(n, 'replace')
+ char, id = is_char(n, font)
+ elseif char then
+ local char_ccc = ccc[char]
+ if not char_ccc then
+ return false
+ elseif char_ccc == 230 then
+ return mapping
+ end
+ n = getnext(n)
+ else
+ return false
+ end
+ end
+ n, after = after
+ until not n
+ return false
+end
+
+local function is_Not_Before_Dot(font, mapping, n, after)
+ mapping = mapping.Not_Before_Dot
+ if not mapping then return false end
+ mapping = mapping._
+ if not mapping then return false end
+ n = getnext(n)
+ repeat
+ while n do
+ local char, id = is_char(n, font)
+ if id == disc then
+ after = getnext(n)
+ n = getfield(n, 'replace')
+ char, id = is_char(n, font)
+ elseif char then
+ local char_ccc = ccc[char]
+ if not char_ccc then
+ return mapping
+ elseif char_ccc == 230 then
+ return char ~= 0x0307 and mapping
+ end
+ n = getnext(n)
+ else
+ return mapping
+ end
+ end
+ n, after = after
+ until not n
+ return mapping
+end
+
+local function is_Language_Mapping(font, mapping, n, after, seen_soft_dotted, seen_I)
+ if not mapping then return false end
+ if seen_soft_dotted then
+ local mapping = mapping.After_Soft_Dotted
+ mapping = mapping and mapping._
+ if mapping then
+ return mapping
+ end
+ end
+ if seen_I then
+ local mapping = mapping.After_I
+ mapping = mapping and mapping._
+ if mapping then
+ return mapping
+ end
+ end
+ return is_More_Above(font, mapping, n, after) or is_Not_Before_Dot(font, mapping, n, after) or mapping._ -- Might be nil
+end
+
+local function process(table)
+ local function processor(head, font, after, seen_cased, seen_soft_dotted, seen_I)
+ local lang = font_lang[font]
+ local n = head
+ while n do
+ do
+ local new = has_glyph(n)
+ if n ~= new then
+ seen_cased, seen_soft_dotted, seen_I = nil
+ end
+ n = new
+ end
+ if not n then break end
+ local char, id = is_char(n, font)
+ if char then
+ local mapping = table[char]
+ if mapping then
+ if tonumber(mapping) then
+ setchar(n, mapping)
+ else
+ mapping = seen_cased and is_Final_Sigma(font, mapping, n, after)
+ or lang and is_Language_Mapping(font, mapping[lang], n, after, seen_soft_dotted, seen_I)
+ or mapping._
+ if #mapping == 0 then
+ head, n = remove(head, n)
+ goto continue
+ else
+ setchar(n, mapping[1])
+ for i=2, #mapping do
+ head, n = insert_after(head, n, copy(n))
+ setchar(n, mapping[i])
+ end
+ end
+ end
+ end
+ if not case_ignorable[char] then
+ seen_cased = cased[char] or nil
+ end
+ local char_ccc = ccc[char]
+ if not char_ccc or char_ccc == 230 then
+ seen_I = char == 0x49 or nil
+ seen_soft_dotted = soft_dotted[char]
+ end
+ elseif id == disc and uses_font(n, font) then
+ local pre, post, rep = getdisc(n)
+ local after = getnext(n)
+ pre, post, rep, seen_cased, seen_soft_dotted, seen_I =
+ processor(pre, font, nil, seen_cased, seen_soft_dotted, seen_I),
+ processor(post, font, after),
+ processor(rep, font, after, seen_cased, seen_soft_dotted, seen_I)
+ setdisc(n, pre, post, rep)
+ else
+ seen_cased, seen_soft_dotted, seen_I = nil
+ end
+ n = getnext(n)
+ ::continue::
+ end
+ return head, seen_cased, seen_soft_dotted, seen_I
+ end
+ return function(head, font) return (processor(head, font)) end
+end
+
+local upper_process = process(uppercase)
+otfregister {
+ name = 'upper',
+ description = 'Map to uppercase',
+ default = false,
+ processors = {
+ position = 1,
+ plug = upper_process,
+ node = upper_process,
+ base = upper_process,
+ },
+}
+
+local lower_process = process(lowercase)
+otfregister {
+ name = 'lower',
+ description = 'Map to lowercase',
+ default = false,
+ processors = {
+ position = 1,
+ plug = lower_process,
+ node = lower_process,
+ base = lower_process,
+ },
+}