mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
ReaderFont: add "Font-family fonts" submenu
Allow associating a font to each of the generic font-family names. Bump crengine: - CSS parsing: accept (and ignore) namesspaces - isImage(): more checks for <object> as it can have inner content - getFontFileNameAndFaceIndex(): returns if font has math support - getFontFileNameAndFaceIndex(): returns if font has emojis - CSS/Fonts: add support for font-family to font name mapping
This commit is contained in:
@@ -5,6 +5,7 @@ local Device = require("device")
|
||||
local Event = require("ui/event")
|
||||
local Font = require("ui/font")
|
||||
local FontList = require("fontlist")
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
local Input = Device.input
|
||||
local InputContainer = require("ui/widget/container/inputcontainer")
|
||||
local Menu = require("ui/widget/menu")
|
||||
@@ -50,6 +51,25 @@ function ReaderFont:init()
|
||||
table.insert(self.face_table, {
|
||||
text = _("Font settings"),
|
||||
sub_item_table = self:getFontSettingsTable(),
|
||||
})
|
||||
table.insert(self.face_table, {
|
||||
text_func = function()
|
||||
local nb_family_fonts = 0
|
||||
local g_font_family_fonts = G_reader_settings:readSetting("cre_font_family_fonts", {})
|
||||
for family, name in pairs(g_font_family_fonts) do
|
||||
if self.font_family_fonts[family] then
|
||||
nb_family_fonts = nb_family_fonts + 1
|
||||
elseif g_font_family_fonts[family] and self.font_family_fonts[family] ~= false then
|
||||
nb_family_fonts = nb_family_fonts + 1
|
||||
end
|
||||
end
|
||||
if nb_family_fonts > 0 then
|
||||
-- @translators 'font-family' is a CSS property name, keep it untranslated
|
||||
return T(_("Font-family fonts (%1)"), nb_family_fonts)
|
||||
end
|
||||
return _("Font-family fonts")
|
||||
end,
|
||||
sub_item_table_func = function() return self:getFontFamiliesTable() end,
|
||||
separator = true,
|
||||
})
|
||||
-- Font list
|
||||
@@ -167,6 +187,9 @@ function ReaderFont:onReadSettings(config)
|
||||
or 15 -- gamma = 1.0
|
||||
self.ui.document:setGammaIndex(self.gamma_index)
|
||||
|
||||
self.font_family_fonts = config:readSetting("font_family_fonts") or {}
|
||||
self:updateFontFamilyFonts()
|
||||
|
||||
-- Dirty hack: we have to add following call in order to set
|
||||
-- m_is_rendered(member of LVDocView) to true. Otherwise position inside
|
||||
-- document will be reset to 0 on first view render.
|
||||
@@ -301,6 +324,7 @@ function ReaderFont:onSaveSettings()
|
||||
self.ui.doc_settings:saveSetting("cjk_width_scaling", self.cjk_width_scaling)
|
||||
self.ui.doc_settings:saveSetting("line_space_percent", self.line_space_percent)
|
||||
self.ui.doc_settings:saveSetting("gamma_index", self.gamma_index)
|
||||
self.ui.doc_settings:saveSetting("font_family_fonts", self.font_family_fonts)
|
||||
end
|
||||
|
||||
function ReaderFont:onSetFont(face)
|
||||
@@ -405,6 +429,267 @@ function ReaderFont:onDecreaseFontSize(ges)
|
||||
return true
|
||||
end
|
||||
|
||||
local font_family_info_text = _([[
|
||||
In HTML/CSS based documents like EPUBs, stylesheets can specify to use fonts by family instead of a specific font name.
|
||||
Except for monospace and math, KOReader uses your default font for any family name.
|
||||
You can associate a specific font to each family if you care about the distinction.
|
||||
A long-press on a font name will make the association global (★), so it applies to all your books. This is the preferred approach.
|
||||
A tap will only affect the current book.
|
||||
If you encounter a book where such families are abused to the point where your default font is hardly used, you can quickly disable a family font for this book by unchecking the association.]])
|
||||
|
||||
local FONT_FAMILIES = {
|
||||
-- On 1st page
|
||||
-- @translators These are typography font family names as used in CSS, they can be kept untranslated if they are used more commonly than their translation
|
||||
{ "serif", _("Serif") },
|
||||
{ "sans-serif", _("Sans-serif") },
|
||||
{ "monospace", _("Monospace") },
|
||||
-- On 2nd page
|
||||
{ "cursive", _("Cursive") },
|
||||
{ "fantasy", _("Fantasy") },
|
||||
{ "emoji", _("Emoji \u{1F60A}") },
|
||||
{ "fangsong", _("Fang Song \u{4EFF}\u{5B8B}") },
|
||||
{ "math", _("Math") },
|
||||
}
|
||||
|
||||
function ReaderFont:updateFontFamilyFonts()
|
||||
-- Note: when no font is specified for a family, we provide an empty string to
|
||||
-- crengine, which is enough to have it kill any "css_ff_inherit" and have the
|
||||
-- font picking code select a new font for a node - which will pick the main
|
||||
-- font (we have here in self.font_face) because of its increased bias (or the
|
||||
-- monospace font we also added with bias).
|
||||
-- So, we don't need to insert self.font_face in the list for unset family fonts,
|
||||
-- which would otherwise need us to call updateFontFamilyFonts() everytime we
|
||||
-- change the main font face.
|
||||
local g_font_family_fonts = G_reader_settings:readSetting("cre_font_family_fonts", {})
|
||||
local family_fonts = {}
|
||||
for i, family in ipairs(FONT_FAMILIES) do
|
||||
local family_tag = family[1]
|
||||
if self.font_family_fonts[family_tag] then
|
||||
family_fonts[family_tag] = self.font_family_fonts[family_tag]
|
||||
elseif g_font_family_fonts[family_tag] and self.font_family_fonts[family_tag] ~= false then
|
||||
family_fonts[family_tag] = g_font_family_fonts[family_tag]
|
||||
elseif family_tag == "math" then
|
||||
-- If no math font set, force using our math-enabled shipped FreeSerif (which
|
||||
-- saves crengine some work iterating its hardcoded list of math fonts).
|
||||
family_fonts[family_tag] = "FreeSerif"
|
||||
end
|
||||
end
|
||||
self.ui.document:setFontFamilyFontFaces(family_fonts, G_reader_settings:isTrue("cre_font_family_ignore_font_names"))
|
||||
self.ui:handleEvent(Event:new("UpdatePos"))
|
||||
end
|
||||
|
||||
function ReaderFont:getFontFamiliesTable()
|
||||
local g_font_family_fonts = G_reader_settings:readSetting("cre_font_family_fonts", {})
|
||||
local families_table = {
|
||||
{
|
||||
text = _("About font-family fonts"),
|
||||
callback = function()
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = font_family_info_text,
|
||||
})
|
||||
end,
|
||||
keep_menu_open = true,
|
||||
separator = true,
|
||||
},
|
||||
{
|
||||
text = _("Ignore publisher font names when font-family is set"),
|
||||
checked_func = function()
|
||||
return G_reader_settings:isTrue("cre_font_family_ignore_font_names")
|
||||
end,
|
||||
callback = function()
|
||||
G_reader_settings:flipNilOrFalse("cre_font_family_ignore_font_names")
|
||||
self:updateFontFamilyFonts()
|
||||
end,
|
||||
help_text = _([[
|
||||
In a CSS font-family declaration, publishers may precede a family name with one or more font names, that are to be used if found among embedded fonts or your own fonts.
|
||||
Enabling this will ignore such font names and make sure your preferred family fonts are used.]]),
|
||||
keep_menu_open = true,
|
||||
separator = true,
|
||||
},
|
||||
max_per_page = 5,
|
||||
}
|
||||
local face_to_filename = {}
|
||||
local face_list = cre.getFontFaces()
|
||||
for i, family in ipairs(FONT_FAMILIES) do
|
||||
local family_tag, family_name = family[1], family[2]
|
||||
-- If none family font is set, crengine will use the main user set font,
|
||||
-- except for 2 specific cases.
|
||||
local unset_font_main_text = _("(main font)")
|
||||
local unset_font_choice_text = _("Use main font")
|
||||
if family_tag == "monospace" then
|
||||
local monospace_font = G_reader_settings:readSetting("monospace_font") or self.ui.document.monospace_font
|
||||
unset_font_main_text = _("(default monospace font)")
|
||||
unset_font_choice_text = T(_("Use default monospace font: %1"), monospace_font)
|
||||
elseif family_tag == "math" then
|
||||
unset_font_main_text = _("(default math font)")
|
||||
unset_font_choice_text = _("Use default math font")
|
||||
-- The default math font would be FreeSerif, but crengine would pick a better
|
||||
-- one among a hardcoded list if any is found. So, don't say more than that.
|
||||
end
|
||||
local family_table = {
|
||||
menu_item_id = family_tag,
|
||||
text_func = function()
|
||||
local text
|
||||
if self.font_family_fonts[family_tag] then
|
||||
text = BD.wrap(self.font_family_fonts[family_tag])
|
||||
elseif g_font_family_fonts[family_tag] then
|
||||
-- Show it even if self.font_family_fonts[family_tag]==false,
|
||||
-- the checkbox will indicate whether it is used or not
|
||||
text = BD.wrap(g_font_family_fonts[family_tag]) .. " ★"
|
||||
else
|
||||
text = unset_font_main_text .. " ★"
|
||||
end
|
||||
return T("%1: %2", family_name, text)
|
||||
end,
|
||||
font_func = function(size)
|
||||
if G_reader_settings:nilOrTrue("font_menu_use_font_face") then
|
||||
local font_name
|
||||
if self.font_family_fonts[family_tag] then
|
||||
font_name = self.font_family_fonts[family_tag]
|
||||
elseif g_font_family_fonts[family_tag] then
|
||||
font_name = g_font_family_fonts[family_tag]
|
||||
end
|
||||
if font_name and face_to_filename[font_name] then
|
||||
local filename_idx = face_to_filename[font_name]
|
||||
return Font:getFace(filename_idx[1], size, filename_idx[2])
|
||||
end
|
||||
end
|
||||
end,
|
||||
checked_func = function()
|
||||
if self.font_family_fonts[family_tag] then
|
||||
return true
|
||||
elseif g_font_family_fonts[family_tag] and self.font_family_fonts[family_tag] ~= false then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end,
|
||||
checkmark_callback = function()
|
||||
if self.font_family_fonts[family_tag] then
|
||||
if g_font_family_fonts[family_tag] then
|
||||
self.font_family_fonts[family_tag] = false
|
||||
else
|
||||
self.font_family_fonts[family_tag] = nil
|
||||
end
|
||||
else
|
||||
if g_font_family_fonts[family_tag] and self.font_family_fonts[family_tag] ~= false then
|
||||
self.font_family_fonts[family_tag] = false
|
||||
else
|
||||
self.font_family_fonts[family_tag] = nil
|
||||
end
|
||||
end
|
||||
self:updateFontFamilyFonts()
|
||||
end,
|
||||
sub_item_table = {
|
||||
{
|
||||
text = T(_("Font for %1"), BD.wrap(T("'font-family: %1'", family_tag))),
|
||||
separator = true,
|
||||
},
|
||||
{
|
||||
text_func = function()
|
||||
local text = unset_font_choice_text
|
||||
if not g_font_family_fonts[family_tag] then
|
||||
text = text .. " ★"
|
||||
end
|
||||
return text
|
||||
end,
|
||||
callback = function()
|
||||
self.font_family_fonts[family_tag] = false
|
||||
self:updateFontFamilyFonts()
|
||||
end,
|
||||
hold_callback = function(touchmenu_instance)
|
||||
g_font_family_fonts[family_tag] = nil
|
||||
self.font_family_fonts[family_tag] = nil
|
||||
self:updateFontFamilyFonts()
|
||||
if touchmenu_instance then touchmenu_instance:updateItems() end
|
||||
end,
|
||||
checked_func = function()
|
||||
if self.font_family_fonts[family_tag] == false then
|
||||
return true
|
||||
end
|
||||
return not self.font_family_fonts[family_tag] and not g_font_family_fonts[family_tag]
|
||||
end,
|
||||
separator = true,
|
||||
},
|
||||
max_per_page = 5,
|
||||
},
|
||||
}
|
||||
for k, v in ipairs(face_list) do
|
||||
local font_filename, font_faceindex, is_monospace, has_ot_math, has_emojis = cre.getFontFaceFilenameAndFaceIndex(v)
|
||||
if i == 1 then
|
||||
face_to_filename[v] = { font_filename, font_faceindex }
|
||||
end
|
||||
local ignore = false
|
||||
if family_tag == "monospace" and not is_monospace then
|
||||
ignore = true
|
||||
end
|
||||
if family_tag == "math" and not has_ot_math then
|
||||
ignore = true
|
||||
end
|
||||
if family_tag == "emoji" and not has_emojis then
|
||||
ignore = true
|
||||
end
|
||||
if not ignore then
|
||||
table.insert(family_table.sub_item_table, {
|
||||
text_func = function()
|
||||
local text = v
|
||||
if font_filename and font_faceindex then
|
||||
text = FontList:getLocalizedFontName(font_filename, font_faceindex) or text
|
||||
end
|
||||
if g_font_family_fonts[family_tag] == v then
|
||||
text = text .. " ★"
|
||||
end
|
||||
return text
|
||||
end,
|
||||
font_func = function(size)
|
||||
if G_reader_settings:nilOrTrue("font_menu_use_font_face") then
|
||||
if font_filename and font_faceindex then
|
||||
return Font:getFace(font_filename, size, font_faceindex)
|
||||
end
|
||||
end
|
||||
end,
|
||||
callback = function()
|
||||
if g_font_family_fonts[family_tag] == v then
|
||||
self.font_family_fonts[family_tag] = nil
|
||||
else
|
||||
self.font_family_fonts[family_tag] = v
|
||||
-- We don't use :notify() as we don't want this notification to be masked,
|
||||
-- to let the user know it's not global (so he has to use long-press)
|
||||
UIManager:show(Notification:new{ text = _("Font family font set for this book only.") })
|
||||
-- Be sure it is shown before the re-rendering (which may take some time)
|
||||
UIManager:forceRePaint()
|
||||
end
|
||||
self:updateFontFamilyFonts()
|
||||
end,
|
||||
hold_callback = function(touchmenu_instance)
|
||||
g_font_family_fonts[family_tag] = v
|
||||
self.font_family_fonts[family_tag] = nil
|
||||
self:updateFontFamilyFonts()
|
||||
if touchmenu_instance then touchmenu_instance:updateItems() end
|
||||
end,
|
||||
checked_func = function()
|
||||
if self.font_family_fonts[family_tag] then
|
||||
return self.font_family_fonts[family_tag] == v
|
||||
elseif g_font_family_fonts[family_tag] == v and self.font_family_fonts[family_tag] ~= false then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end,
|
||||
menu_item_id = family_tag .. "_" .. v,
|
||||
})
|
||||
end
|
||||
end
|
||||
family_table.sub_item_table.open_on_menu_item_id_func = function()
|
||||
if self.font_family_fonts[family_tag] then
|
||||
return family_tag .. "_" .. self.font_family_fonts[family_tag]
|
||||
elseif g_font_family_fonts[family_tag] and self.font_family_fonts[family_tag] ~= false then
|
||||
return family_tag .. "_" .. g_font_family_fonts[family_tag]
|
||||
end
|
||||
end
|
||||
table.insert(families_table, family_table)
|
||||
end
|
||||
return families_table
|
||||
end
|
||||
|
||||
function ReaderFont:getFontSettingsTable()
|
||||
local settings_table = {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user