|
|
local FFIUtil = require("ffi/util")
|
|
|
local Font = require("ui/font")
|
|
|
local FontList = require("fontlist")
|
|
|
local InfoMessage = require("ui/widget/infomessage")
|
|
|
local UIManager = require("ui/uimanager")
|
|
|
local util = require("util")
|
|
|
local _ = require("gettext")
|
|
|
local T = FFIUtil.template
|
|
|
|
|
|
-- Some "Noto Sans *" fonts are already in ui/font.lua Font.fallbacks,
|
|
|
-- because they are required to display supported UI languages.
|
|
|
-- Don't allow them to be disabled.
|
|
|
-- Duplicated from (and should be kept in sync with) ui/font.lua, but here
|
|
|
-- by face name and there by font file/path name.
|
|
|
local hardcoded_fallbacks = {
|
|
|
"Noto Sans CJK SC",
|
|
|
"Noto Sans Arabic UI",
|
|
|
"Noto Sans Devanagari UI",
|
|
|
"Noto Sans Bengali UI",
|
|
|
}
|
|
|
-- Add any user font after Noto Sans CJK SC in the menu
|
|
|
local additional_fallback_insert_indice = 2 -- (indice in the above list)
|
|
|
|
|
|
local alt_name = {
|
|
|
-- Make it clear this "CJK SC" font actually supports all other CJK variant
|
|
|
["Noto Sans CJK SC"] = "Noto Sans CJK SC (TC, JA, KO)"
|
|
|
}
|
|
|
|
|
|
local fallback_candidates = nil
|
|
|
local fallback_candidates_path_to_name = nil
|
|
|
|
|
|
local genFallbackCandidates = function()
|
|
|
if fallback_candidates then
|
|
|
return
|
|
|
end
|
|
|
fallback_candidates = {}
|
|
|
fallback_candidates_path_to_name = {}
|
|
|
for _, font_path in ipairs(FontList:getFontList()) do
|
|
|
local fontinfo = FontList.fontinfo[font_path] -- (NotoColorEmoji.tff happens to get no fontinfo)
|
|
|
if fontinfo and #fontinfo == 1 then -- Ignore font files with multiple faces
|
|
|
fontinfo = fontinfo[1]
|
|
|
if util.stringStartsWith(fontinfo.name, "Noto Sans ") and
|
|
|
not fontinfo.bold and not fontinfo.italic and
|
|
|
not fontinfo.serif and not fontinfo.mono then
|
|
|
fallback_candidates[fontinfo.name] = fontinfo
|
|
|
fallback_candidates_path_to_name[font_path] = fontinfo.name
|
|
|
end
|
|
|
end
|
|
|
end
|
|
|
end
|
|
|
|
|
|
local more_info_text = _([[
|
|
|
If some book titles, dictionary entries and such are not displayed well but shown as or <20><>, it may be necessary to download the required fonts for those languages. They can then be enabled as additional UI fallback fonts.
|
|
|
Fonts for many languages can be downloaded at:
|
|
|
|
|
|
https://fonts.google.com/noto
|
|
|
|
|
|
Only fonts named "Noto Sans xyz" or "Noto Sans xyz UI" (regular, not bold nor italic, not Serif) will be available in this menu.]])
|
|
|
|
|
|
local getSubMenuItems = function()
|
|
|
genFallbackCandidates()
|
|
|
-- Order the menu items in the order the fallback fonts are used
|
|
|
local seen_names = {}
|
|
|
local ordered_names = {}
|
|
|
local checked_names = {}
|
|
|
local enabled_names = {}
|
|
|
for _, name in ipairs(hardcoded_fallbacks) do
|
|
|
table.insert(ordered_names, name)
|
|
|
seen_names[name] = true
|
|
|
checked_names[name] = true
|
|
|
enabled_names[name] = false
|
|
|
end
|
|
|
if G_reader_settings:has("font_ui_fallbacks") then
|
|
|
local additional_fallbacks = G_reader_settings:readSetting("font_ui_fallbacks")
|
|
|
for i=#additional_fallbacks, 1, -1 do
|
|
|
local path = additional_fallbacks[i]
|
|
|
local name = fallback_candidates_path_to_name[path]
|
|
|
if not name or seen_names[name] then
|
|
|
-- No longer found, or made hardcoded: remove it
|
|
|
table.remove(additional_fallbacks, i)
|
|
|
else
|
|
|
table.insert(ordered_names, additional_fallback_insert_indice, name)
|
|
|
seen_names[name] = true
|
|
|
checked_names[name] = true
|
|
|
enabled_names[name] = true
|
|
|
end
|
|
|
end
|
|
|
if #additional_fallbacks == 0 then -- all removed
|
|
|
G_reader_settings:delSetting("font_ui_fallbacks")
|
|
|
end
|
|
|
end
|
|
|
local add_separator_idx = #ordered_names
|
|
|
for name, fontinfo in FFIUtil.orderedPairs(fallback_candidates) do
|
|
|
if not seen_names[name] then
|
|
|
table.insert(ordered_names, name)
|
|
|
checked_names[name] = false
|
|
|
enabled_names[name] = true
|
|
|
end
|
|
|
end
|
|
|
|
|
|
local menu_items = {
|
|
|
{
|
|
|
text = _("About additional UI fallback fonts"),
|
|
|
callback = function()
|
|
|
UIManager:show(InfoMessage:new{
|
|
|
text = more_info_text,
|
|
|
})
|
|
|
end,
|
|
|
keep_menu_open = true,
|
|
|
separator = true,
|
|
|
}
|
|
|
}
|
|
|
for idx, name in ipairs(ordered_names) do
|
|
|
local fontinfo = fallback_candidates[name]
|
|
|
local item = {
|
|
|
text = alt_name[name] or name,
|
|
|
separator = idx == add_separator_idx,
|
|
|
checked_func = function()
|
|
|
return checked_names[name]
|
|
|
end,
|
|
|
enabled_func = function()
|
|
|
return enabled_names[name]
|
|
|
end,
|
|
|
callback = function()
|
|
|
local additional_fallbacks = G_reader_settings:readSetting("font_ui_fallbacks", {})
|
|
|
if checked_names[name] then -- enabled: remove it
|
|
|
for i=#additional_fallbacks, 1, -1 do
|
|
|
if additional_fallbacks[i] == fontinfo.path then
|
|
|
table.remove(additional_fallbacks, i)
|
|
|
if #additional_fallbacks == 0 then
|
|
|
G_reader_settings:delSetting("font_ui_fallbacks")
|
|
|
end
|
|
|
break
|
|
|
end
|
|
|
end
|
|
|
checked_names[name] = false
|
|
|
else -- disabled: add it
|
|
|
if #additional_fallbacks < Font.additional_fallback_max_nb then
|
|
|
table.insert(additional_fallbacks, 1, fontinfo.path)
|
|
|
checked_names[name] = true
|
|
|
else
|
|
|
UIManager:show(InfoMessage:new{
|
|
|
text = T(_("The number of allowed additional fallback fonts is limited to %1.\nUncheck some of them if you want to add this one."), Font.additional_fallback_max_nb),
|
|
|
})
|
|
|
return
|
|
|
end
|
|
|
end
|
|
|
UIManager:show(InfoMessage:new{
|
|
|
text = _("This will take effect on next restart."),
|
|
|
})
|
|
|
end,
|
|
|
}
|
|
|
table.insert(menu_items, item)
|
|
|
end
|
|
|
return menu_items
|
|
|
end
|
|
|
|
|
|
return {
|
|
|
text = _("Additional UI fallback fonts"),
|
|
|
sub_item_table_func = getSubMenuItems,
|
|
|
}
|