mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
UI font fallbacks: allow using more "Noto Sans xyz" (#8722)
Add a submenu allowing users to add Noto Sans fonts to be used as UI fallback fonts. Lots of them are alrady there on Android, but can be downloaded (manually) and side-loaded by the user on other platforms.
This commit is contained in:
@@ -580,6 +580,8 @@ common_settings.document_end_action = {
|
||||
|
||||
common_settings.language = Language:getLangMenuTable()
|
||||
|
||||
common_settings.font_ui_fallbacks = require("ui/elements/font_ui_fallbacks")
|
||||
|
||||
common_settings.screenshot = {
|
||||
text = _("Screenshot folder"),
|
||||
callback = function()
|
||||
|
||||
@@ -43,6 +43,8 @@ local order = {
|
||||
},
|
||||
device = {
|
||||
"keyboard_layout",
|
||||
"font_ui_fallbacks",
|
||||
"----------------------------",
|
||||
"time",
|
||||
"device_status_alarm",
|
||||
"charging_led", -- if Device:canToggleChargingLED()
|
||||
|
||||
160
frontend/ui/elements/font_ui_fallbacks.lua
Normal file
160
frontend/ui/elements/font_ui_fallbacks.lua
Normal file
@@ -0,0 +1,160 @@
|
||||
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",
|
||||
}
|
||||
-- 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,
|
||||
}
|
||||
@@ -84,6 +84,8 @@ local order = {
|
||||
},
|
||||
device = {
|
||||
"keyboard_layout",
|
||||
"font_ui_fallbacks",
|
||||
"----------------------------",
|
||||
"time",
|
||||
"device_status_alarm",
|
||||
"charging_led", -- if Device:canToggleChargingLED()
|
||||
|
||||
@@ -113,11 +113,27 @@ local Font = {
|
||||
[6] = "freefont/FreeSans.ttf",
|
||||
[7] = "freefont/FreeSerif.ttf",
|
||||
},
|
||||
-- Additional fallback fonts are managed by frontend/ui/elements/font_ui_fallbacks.lua
|
||||
-- Add any after NotoSansCJKsc (because CJKsc has better symbols, and has 'locl' OTF
|
||||
-- features to support all of SC, TC, JA and KO that other CJK fonts may not have.)
|
||||
additional_fallback_insert_indice = 3,
|
||||
-- Xtext supports up to 15 fallback fonts, but keep some slots free and available for
|
||||
-- future additions to our hardcoded fallbacks list above, and to not slow down
|
||||
-- rendering with too many fallback fonts.
|
||||
additional_fallback_max_nb = 4,
|
||||
|
||||
-- face table
|
||||
faces = {},
|
||||
}
|
||||
|
||||
if G_reader_settings and 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
|
||||
table.insert(Font.fallbacks, Font.additional_fallback_insert_indice, additional_fallbacks[i])
|
||||
end
|
||||
logger.dbg("updated Font.fallbacks:", Font.fallbacks)
|
||||
end
|
||||
|
||||
-- Helper functions with explicite names around
|
||||
-- bold/regular_font_variant tables
|
||||
function Font:hasBoldVariant(name)
|
||||
|
||||
Reference in New Issue
Block a user