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 ��, 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, }