2020-01-04 01:18:51 +01:00
local BD = require ( " ui/bidi " )
2018-05-11 17:48:26 +02:00
local ConfirmBox = require ( " ui/widget/confirmbox " )
2014-10-30 19:42:18 +01:00
local Device = require ( " device " )
2013-10-22 17:11:31 +02:00
local Event = require ( " ui/event " )
2020-10-05 20:26:33 +02:00
local Font = require ( " ui/font " )
2020-10-20 06:30:41 +02:00
local FontList = require ( " fontlist " )
2022-10-03 17:28:07 +02:00
local InfoMessage = require ( " ui/widget/infomessage " )
2017-04-22 09:18:10 +02:00
local Input = Device.input
local InputContainer = require ( " ui/widget/container/inputcontainer " )
local MultiConfirmBox = require ( " ui/widget/multiconfirmbox " )
local Notification = require ( " ui/widget/notification " )
local Screen = require ( " device " ) . screen
2013-10-18 22:38:07 +02:00
local UIManager = require ( " ui/uimanager " )
2022-09-28 01:10:50 +02:00
local cre -- Delayed loading
2023-07-12 22:29:13 +02:00
local logger = require ( " logger " )
local util = require ( " util " )
2013-10-18 22:38:07 +02:00
local _ = require ( " gettext " )
2023-07-12 22:29:13 +02:00
local T = require ( " ffi/util " ) . template
2019-08-24 09:25:38 +02:00
local C_ = _.pgettext
2021-05-20 23:14:11 +02:00
local optionsutil = require ( " ui/data/optionsutil " )
2013-10-18 22:38:07 +02:00
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 02:14:48 +02:00
local ReaderFont = InputContainer : extend {
2014-03-13 21:52:43 +08:00
font_face = nil ,
2017-09-13 12:44:37 +02:00
font_menu_title = _ ( " Font " ) ,
2014-03-13 21:52:43 +08:00
face_table = nil ,
2017-06-29 20:38:39 +02:00
steps = { 0 , 1 , 1 , 1 , 1 , 1 , 2 , 2 , 2 , 3 , 3 , 3 , 4 , 4 , 5 } ,
2012-06-12 18:04:08 +08:00
}
2023-07-12 22:29:13 +02:00
-- Keep a list of the new fonts seen at launch
local newly_added_fonts = nil -- not yet filled
2012-06-12 20:58:15 +08:00
function ReaderFont : init ( )
2022-11-01 23:22:07 +01:00
self : registerKeyEvents ( )
2023-07-12 22:29:13 +02:00
self : setupFaceMenuTable ( )
self.ui . menu : registerToMainMenu ( self )
-- NOP our own gesture handling
self.ges_events = nil
end
function ReaderFont : setupFaceMenuTable ( )
logger.dbg ( " building font face menu table " )
2020-04-28 23:57:10 +02:00
-- Build face_table for menu
2014-03-13 21:52:43 +08:00
self.face_table = { }
2020-04-28 23:57:10 +02:00
-- Font settings
table.insert ( self.face_table , {
text = _ ( " Font settings " ) ,
sub_item_table = self : getFontSettingsTable ( ) ,
2022-10-03 17:28:07 +02:00
} )
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 ,
2020-04-28 23:57:10 +02:00
separator = true ,
} )
-- Font list
2022-09-28 01:10:50 +02:00
cre = require ( " document/credocument " ) : engineInit ( )
2014-03-13 21:52:43 +08:00
local face_list = cre.getFontFaces ( )
2023-07-12 22:29:13 +02:00
face_list = self : sortFaceList ( face_list )
2022-09-28 01:10:50 +02:00
for k , v in ipairs ( face_list ) do
2022-07-18 01:16:51 +02:00
local font_filename , font_faceindex , is_monospace = cre.getFontFaceFilenameAndFaceIndex ( v )
2024-08-18 07:28:23 +00:00
if not font_filename then
-- The font may be available only in italic, for example script/cursive fonts
font_filename , font_faceindex , is_monospace = cre.getFontFaceFilenameAndFaceIndex ( v , nil , true )
end
2014-03-13 21:52:43 +08:00
table.insert ( self.face_table , {
2018-05-11 17:48:26 +02:00
text_func = function ( )
-- defaults are hardcoded in credocument.lua
local default_font = G_reader_settings : readSetting ( " cre_font " ) or self.ui . document.default_font
2020-04-25 20:20:41 +02:00
local fallback_font = G_reader_settings : readSetting ( " fallback_font " ) or self.ui . document.fallback_fonts [ 1 ]
2022-07-18 01:16:51 +02:00
local monospace_font = G_reader_settings : readSetting ( " monospace_font " ) or self.ui . document.monospace_font
2018-05-11 17:48:26 +02:00
local text = v
2020-10-20 06:30:41 +02:00
if font_filename and font_faceindex then
text = FontList : getLocalizedFontName ( font_filename , font_faceindex ) or text
end
2022-07-18 01:16:51 +02:00
if v == monospace_font then
text = text .. " \u{1F13C} " -- Squared Latin Capital Letter M
elseif is_monospace then
text = text .. " \u{1D39} " -- Modified Letter Capital M
end
2018-05-11 17:48:26 +02:00
if v == default_font then
text = text .. " ★ "
end
if v == fallback_font then
text = text .. " <20> "
end
2023-07-12 22:29:13 +02:00
if newly_added_fonts [ v ] then
text = text .. " \u{EA93} " -- "NEW" in a black square, from nerdfont
end
2018-05-11 17:48:26 +02:00
return text
end ,
2020-10-05 20:26:33 +02:00
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 ,
2014-03-13 21:52:43 +08:00
callback = function ( )
2021-12-16 15:31:22 -05:00
self : onSetFont ( v )
2023-07-12 22:29:13 +02:00
-- We add it to the recently selected list only for tap on the
-- menu item (and not when :onSetFont() would be triggered by
-- a gesture/profile), which may be convenient for some users.
self : addToRecentlySelectedList ( v )
2014-07-02 22:53:06 +08:00
end ,
2018-09-04 23:55:58 +02:00
hold_callback = function ( touchmenu_instance )
2022-07-18 01:16:51 +02:00
self : makeDefault ( v , is_monospace , touchmenu_instance )
2014-07-02 22:53:06 +08:00
end ,
2014-11-15 16:23:27 +01:00
checked_func = function ( )
return v == self.font_face
2021-04-02 18:12:06 +02:00
end ,
menu_item_id = v ,
2014-03-13 21:52:43 +08:00
} )
end
2023-07-12 22:29:13 +02:00
self.face_table . refresh_func = function ( )
self : setupFaceMenuTable ( )
-- This might be used by TouchMenu to refresh its font list menu,
-- so return the newly created menu table.
return self.face_table
end
2021-04-02 18:12:06 +02:00
self.face_table . open_on_menu_item_id_func = function ( )
return self.font_face
end
2023-07-12 22:29:13 +02:00
-- Have TouchMenu show half of the usual nb of items, so we
-- have more room to see how the text looks with that font
self.face_table . max_per_page = 5
2012-06-27 00:59:47 +08:00
end
2022-10-30 23:05:04 +01:00
function ReaderFont : onGesture ( ) end
2022-11-01 23:22:07 +01:00
function ReaderFont : registerKeyEvents ( )
2022-11-01 00:17:25 +01:00
if Device : hasKeyboard ( ) then
2024-05-26 21:25:54 +02:00
if not ( Device : hasScreenKB ( ) or Device : hasSymKey ( ) ) then
-- add shortcut for keyboard
self.key_events . IncreaseSize = {
2022-11-01 00:17:25 +01:00
{ " Shift " , Input.group . PgFwd } ,
event = " ChangeSize " ,
args = 0.5
2024-05-26 21:25:54 +02:00
}
self.key_events . DecreaseSize = {
2022-11-01 00:17:25 +01:00
{ " Shift " , Input.group . PgBack } ,
event = " ChangeSize " ,
args = - 0.5
2024-05-26 21:25:54 +02:00
}
end
2022-11-01 00:17:25 +01:00
end
end
ReaderFont.onPhysicalKeyboardConnected = ReaderFont.registerKeyEvents
2012-06-27 00:59:47 +08:00
function ReaderFont : onSetDimensions ( dimen )
2014-03-13 21:52:43 +08:00
self.dimen = dimen
2012-06-27 00:59:47 +08:00
end
function ReaderFont : onReadSettings ( config )
2014-08-17 21:45:48 +08:00
self.font_face = config : readSetting ( " font_face " )
2021-03-06 22:44:18 +01:00
or G_reader_settings : readSetting ( " cre_font " )
or self.ui . document.default_font
2014-03-13 21:52:43 +08:00
self.ui . document : setFontFace ( self.font_face )
2024-02-18 08:06:50 +02:00
local header_font = G_reader_settings : readSetting ( " header_font " ) or self.ui . document.header_font
self.ui . document : setHeaderFont ( header_font )
2014-03-13 21:52:43 +08:00
2023-09-09 09:24:05 +03:00
self.ui . document : setFontSize ( Screen : scaleBySize ( self.configurable . font_size ) )
2023-09-09 12:44:56 +03:00
self.ui . document : setFontBaseWeight ( self.configurable . font_base_weight )
self.ui . document : setFontHinting ( self.configurable . font_hinting )
self.ui . document : setFontKerning ( self.configurable . font_kerning )
self.ui . document : setWordSpacing ( self.configurable . word_spacing )
self.ui . document : setWordExpansion ( self.configurable . word_expansion )
self.ui . document : setCJKWidthScaling ( self.configurable . cjk_width_scaling )
2023-09-08 08:40:47 +03:00
self.ui . document : setInterlineSpacePercent ( self.configurable . line_spacing )
2023-09-09 12:44:56 +03:00
self.ui . document : setGammaIndex ( self.configurable . font_gamma )
2014-03-13 21:52:43 +08:00
2022-10-03 17:28:07 +02:00
self.font_family_fonts = config : readSetting ( " font_family_fonts " ) or { }
self : updateFontFamilyFonts ( )
2016-10-04 10:38:32 -07:00
-- Dirty hack: we have to add following call in order to set
2014-03-13 21:52:43 +08:00
-- m_is_rendered(member of LVDocView) to true. Otherwise position inside
-- document will be reset to 0 on first view render.
-- So far, I don't know why this call will alter the value of m_is_rendered.
table.insert ( self.ui . postInitCallback , function ( )
self.ui : handleEvent ( Event : new ( " UpdatePos " ) )
end )
2012-06-12 20:58:15 +08:00
end
2012-12-22 13:27:46 +08:00
--[[
2014-03-13 21:52:43 +08:00
UpdatePos event is used to tell ReaderRolling to update pos .
2012-12-22 13:27:46 +08:00
--]]
2021-05-20 23:14:11 +02:00
function ReaderFont : onChangeSize ( delta )
2023-09-09 09:24:05 +03:00
self : onSetFontSize ( self.configurable . font_size + delta )
2014-03-13 21:52:43 +08:00
return true
2012-06-12 18:04:08 +08:00
end
2023-09-09 09:24:05 +03:00
function ReaderFont : onSetFontSize ( size )
size = math.max ( 12 , math.min ( size , 255 ) )
self.configurable . font_size = size
self.ui . document : setFontSize ( Screen : scaleBySize ( size ) )
2017-04-22 09:18:10 +02:00
self.ui : handleEvent ( Event : new ( " UpdatePos " ) )
2023-09-09 09:24:05 +03:00
Notification : notify ( T ( _ ( " Font size set to: %1. " ) , size ) )
2014-03-13 21:52:43 +08:00
return true
2013-01-06 23:21:11 -05:00
end
2014-08-17 21:45:48 +08:00
function ReaderFont : onSetLineSpace ( space )
2023-09-08 08:40:47 +03:00
space = math.max ( 50 , math.min ( space , 200 ) )
self.configurable . line_spacing = space
self.ui . document : setInterlineSpacePercent ( space )
2014-03-13 21:52:43 +08:00
self.ui : handleEvent ( Event : new ( " UpdatePos " ) )
2023-09-08 08:40:47 +03:00
Notification : notify ( T ( _ ( " Line spacing set to: %1%. " ) , space ) )
2014-03-13 21:52:43 +08:00
return true
2012-06-12 21:12:04 +08:00
end
2021-04-29 01:37:53 +02:00
function ReaderFont : onSetFontBaseWeight ( weight )
2023-09-09 12:44:56 +03:00
self.configurable . font_base_weight = weight
2021-04-29 01:37:53 +02:00
self.ui . document : setFontBaseWeight ( weight )
2014-03-13 21:52:43 +08:00
self.ui : handleEvent ( Event : new ( " UpdatePos " ) )
2021-05-31 22:19:24 +02:00
Notification : notify ( T ( _ ( " Font weight set to: %1. " ) , optionsutil : getOptionText ( " SetFontBaseWeight " , weight ) ) )
2014-03-13 21:52:43 +08:00
return true
2013-01-07 12:17:34 -05:00
end
2017-09-08 17:38:51 +02:00
function ReaderFont : onSetFontHinting ( mode )
2023-09-09 12:44:56 +03:00
self.configurable . font_hinting = mode
2017-09-08 17:38:51 +02:00
self.ui . document : setFontHinting ( mode )
self.ui : handleEvent ( Event : new ( " UpdatePos " ) )
2021-05-31 22:19:24 +02:00
Notification : notify ( T ( _ ( " Font hinting set to: %1 " ) , optionsutil : getOptionText ( " SetFontHinting " , mode ) ) )
2017-09-08 17:38:51 +02:00
return true
end
2018-08-17 23:08:21 +02:00
function ReaderFont : onSetFontKerning ( mode )
2023-09-09 12:44:56 +03:00
self.configurable . font_kerning = mode
2018-08-17 23:08:21 +02:00
self.ui . document : setFontKerning ( mode )
self.ui : handleEvent ( Event : new ( " UpdatePos " ) )
2021-05-31 22:19:24 +02:00
Notification : notify ( T ( _ ( " Font kerning set to: %1 " ) , optionsutil : getOptionText ( " SetFontKerning " , mode ) ) )
2018-08-17 23:08:21 +02:00
return true
end
2019-11-28 22:39:06 +01:00
function ReaderFont : onSetWordSpacing ( values )
2023-09-09 12:44:56 +03:00
self.configurable . word_spacing = values
2019-11-28 22:39:06 +01:00
self.ui . document : setWordSpacing ( values )
2018-06-30 16:55:43 +02:00
self.ui : handleEvent ( Event : new ( " UpdatePos " ) )
2021-05-31 22:19:24 +02:00
Notification : notify ( T ( _ ( " Word spacing set to: %1%, %2% " ) , values [ 1 ] , values [ 2 ] ) )
2018-06-30 16:55:43 +02:00
return true
end
2020-05-03 18:06:58 +02:00
function ReaderFont : onSetWordExpansion ( value )
2023-09-09 12:44:56 +03:00
self.configurable . word_expansion = value
2020-05-03 18:06:58 +02:00
self.ui . document : setWordExpansion ( value )
self.ui : handleEvent ( Event : new ( " UpdatePos " ) )
2021-05-31 22:19:24 +02:00
Notification : notify ( T ( _ ( " Word expansion set to: %1%. " ) , value ) )
2020-05-03 18:06:58 +02:00
return true
end
2022-06-05 12:36:33 +02:00
function ReaderFont : onSetCJKWidthScaling ( value )
2023-09-09 12:44:56 +03:00
self.configurable . cjk_width_scaling = value
2022-06-05 12:36:33 +02:00
self.ui . document : setCJKWidthScaling ( value )
self.ui : handleEvent ( Event : new ( " UpdatePos " ) )
Notification : notify ( T ( _ ( " CJK width scaling set to: %1%. " ) , value ) )
return true
end
2014-08-17 21:45:48 +08:00
function ReaderFont : onSetFontGamma ( gamma )
2023-09-09 12:44:56 +03:00
self.configurable . font_gamma = gamma
self.ui . document : setGammaIndex ( gamma )
2018-03-11 14:54:17 +01:00
local gamma_level = self.ui . document : getGammaLevel ( )
2014-03-13 21:52:43 +08:00
self.ui : handleEvent ( Event : new ( " RedrawCurrentView " ) )
2021-07-05 11:03:27 +02:00
Notification : notify ( T ( _ ( " Font gamma set to: %1. " ) , gamma_level ) )
2014-03-13 21:52:43 +08:00
return true
2013-01-07 16:42:23 -05:00
end
2013-12-27 23:18:16 +08:00
function ReaderFont : onSaveSettings ( )
2014-03-13 21:52:43 +08:00
self.ui . doc_settings : saveSetting ( " font_face " , self.font_face )
2022-10-03 17:28:07 +02:00
self.ui . doc_settings : saveSetting ( " font_family_fonts " , self.font_family_fonts )
2012-06-26 17:05:49 +08:00
end
2012-12-14 02:32:16 +08:00
2021-12-16 15:31:22 -05:00
function ReaderFont : onSetFont ( face )
2014-03-13 21:52:43 +08:00
if face and self.font_face ~= face then
self.font_face = face
self.ui . document : setFontFace ( face )
-- signal readerrolling to update pos in new height
self.ui : handleEvent ( Event : new ( " UpdatePos " ) )
end
2012-12-14 02:32:16 +08:00
end
2022-07-18 01:16:51 +02:00
function ReaderFont : makeDefault ( face , is_monospace , touchmenu_instance )
2014-07-02 22:53:06 +08:00
if face then
2022-07-18 01:16:51 +02:00
if is_monospace then
-- If the font is monospace, assume it wouldn't be a candidate
-- to be set as a fallback font, and allow it to be set as the
-- default monospace font.
UIManager : show ( MultiConfirmBox : new {
2024-01-12 17:00:45 +01:00
text = T ( _ ( " Would you like %1 to be used as the default font (★), or the monospace font (🄼)? " ) , face ) , -- [M] is U+1F13C
2022-07-18 01:16:51 +02:00
choice1_text = _ ( " Default " ) ,
choice1_callback = function ( )
G_reader_settings : saveSetting ( " cre_font " , face )
if touchmenu_instance then touchmenu_instance : updateItems ( ) end
end ,
choice2_text = C_ ( " Font " , " Monospace " ) ,
choice2_callback = function ( )
G_reader_settings : saveSetting ( " monospace_font " , face )
-- We need to reset the main font for the biases to be re-set correctly
local current_face = self.font_face
self.font_face = nil
self : onSetFont ( current_face )
if touchmenu_instance then touchmenu_instance : updateItems ( ) end
end ,
} )
return
end
2015-09-03 00:51:38 +09:30
UIManager : show ( MultiConfirmBox : new {
2021-05-20 23:14:11 +02:00
text = T ( _ ( " Would you like %1 to be used as the default font (★), or the fallback font (<28> )? \n \n Characters not found in the active font are shown in the fallback font instead. " ) , face ) ,
2015-09-04 10:59:07 +02:00
choice1_text = _ ( " Default " ) ,
2015-09-03 00:51:38 +09:30
choice1_callback = function ( )
2014-07-02 22:53:06 +08:00
G_reader_settings : saveSetting ( " cre_font " , face )
2018-09-04 23:55:58 +02:00
if touchmenu_instance then touchmenu_instance : updateItems ( ) end
2014-07-02 22:53:06 +08:00
end ,
2019-08-24 09:25:38 +02:00
choice2_text = C_ ( " Font " , " Fallback " ) ,
2015-09-03 00:51:38 +09:30
choice2_callback = function ( )
2020-04-25 20:20:41 +02:00
G_reader_settings : saveSetting ( " fallback_font " , face )
self.ui . document : setupFallbackFontFaces ( )
self.ui : handleEvent ( Event : new ( " UpdatePos " ) )
2018-09-04 23:55:58 +02:00
if touchmenu_instance then touchmenu_instance : updateItems ( ) end
2015-09-03 00:51:38 +09:30
end ,
2014-07-02 22:53:06 +08:00
} )
end
end
2017-03-04 14:46:38 +01:00
function ReaderFont : addToMainMenu ( menu_items )
2014-03-13 21:52:43 +08:00
-- insert table to main reader menu
2017-03-04 14:46:38 +01:00
menu_items.change_font = {
2021-04-02 18:12:06 +02:00
text_func = function ( )
return T ( _ ( " Font: %1 " ) , BD.wrap ( self.font_face ) )
end ,
2023-07-12 22:29:13 +02:00
sub_item_table_func = function ( )
if self.face_table . needs_refresh and self.face_table . refresh_func then
self.face_table . refresh_func ( )
end
return self.face_table
end
2017-02-28 22:46:32 +01:00
}
2012-12-15 09:23:02 +08:00
end
2013-10-18 22:38:07 +02:00
2020-07-12 14:47:49 -04:00
function ReaderFont : gesToFontSize ( ges )
2022-09-21 23:26:22 +02:00
-- Dispatcher feeds us a number, not a gesture
2020-07-14 17:39:03 -04:00
if type ( ges ) ~= " table " then return ges end
2022-09-21 23:26:22 +02:00
2019-08-07 20:18:36 +02:00
if ges.distance == nil then
ges.distance = 1
end
2022-09-21 23:26:22 +02:00
-- Compute the scaling based on the gesture's direction (for pinch/spread)
local step
if ges.direction and ges.direction == " vertical " then
step = math.ceil ( 2 * # self.steps * ges.distance / Screen : getHeight ( ) )
elseif ges.direction and ges.direction == " horizontal " then
step = math.ceil ( 2 * # self.steps * ges.distance / Screen : getWidth ( ) )
elseif ges.direction and ges.direction == " diagonal " then
local screen_diagonal = math.sqrt ( Screen : getWidth ( ) ^ 2 + Screen : getHeight ( ) ^ 2 )
step = math.ceil ( 2 * # self.steps * ges.distance / screen_diagonal )
else
step = math.ceil ( 2 * # self.steps * ges.distance / math.min ( Screen : getWidth ( ) , Screen : getHeight ( ) ) )
end
2017-06-29 20:38:39 +02:00
local delta_int = self.steps [ step ] or self.steps [ # self.steps ]
2020-07-12 14:47:49 -04:00
return delta_int
end
function ReaderFont : onIncreaseFontSize ( ges )
local delta_int = self : gesToFontSize ( ges )
2023-02-07 01:01:05 +01:00
Notification : notify ( _ ( " Increasing font size… " ) , nil , true )
2021-05-20 23:14:11 +02:00
self : onChangeSize ( delta_int )
2020-07-12 14:47:49 -04:00
return true
end
function ReaderFont : onDecreaseFontSize ( ges )
local delta_int = self : gesToFontSize ( ges )
2023-02-07 01:01:05 +01:00
Notification : notify ( _ ( " Decreasing font size… " ) , nil , true )
2021-05-20 23:14:11 +02:00
self : onChangeSize ( - delta_int )
2017-04-22 09:18:10 +02:00
return true
end
2022-10-03 17:28:07 +02:00
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 " ) } ,
2024-01-12 17:00:45 +01:00
{ " emoji " , _ ( " Emoji " ) .. " 😊 " } , -- U+1F60A
{ " fangsong " , _ ( " Fang Song " ) .. " 仿宋 " } , -- U+4EFF U+5B8B
2022-10-03 17:28:07 +02:00
{ " 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 = {
2022-12-18 01:25:41 +01:00
ignored_by_menu_search = true , -- those would be duplicated
2022-10-03 17:28:07 +02:00
{
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 )
2024-08-18 07:28:23 +00:00
if not font_filename then
font_filename , font_faceindex , is_monospace , has_ot_math , has_emojis = cre.getFontFaceFilenameAndFaceIndex ( v , nil , true )
end
2022-10-03 17:28:07 +02:00
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
2020-04-28 23:57:10 +02:00
function ReaderFont : getFontSettingsTable ( )
local settings_table = { }
2020-08-25 23:13:39 +02:00
if Device : isAndroid ( ) or Device : isDesktop ( ) or Device : isEmulator ( ) or Device : isPocketBook ( ) then
2020-04-28 23:57:10 +02:00
for _ , item in ipairs ( require ( " ui/elements/font_settings " ) : getSystemFontMenuItems ( ) ) do
table.insert ( settings_table , item )
end
settings_table [ # settings_table ] . separator = true
end
2020-10-05 20:26:33 +02:00
table.insert ( settings_table , {
text = _ ( " Display font names with their own font " ) ,
checked_func = function ( )
return G_reader_settings : nilOrTrue ( " font_menu_use_font_face " )
end ,
callback = function ( )
G_reader_settings : flipNilOrTrue ( " font_menu_use_font_face " )
end ,
help_text = _ ( [[In the font menu, display each font name with its own font face.]] ) ,
2023-07-12 22:29:13 +02:00
} )
table.insert ( settings_table , {
text = _ ( " Sort fonts by recently selected " ) ,
checked_func = function ( )
return G_reader_settings : isTrue ( " font_menu_sort_by_recently_selected " )
end ,
callback = function ( )
G_reader_settings : flipTrue ( " font_menu_sort_by_recently_selected " )
self.face_table . needs_refresh = true
end ,
hold_callback = function ( )
UIManager : show ( ConfirmBox : new {
text = _ ( [ [
The font list menu can show fonts sorted by name or by most recently selected .
New fonts discovered at KOReader startup will be shown first .
Do you want to clear the history of selected fonts ? ] ] ) ,
ok_text = _ ( " Clear " ) ,
ok_callback = function ( )
G_reader_settings : delSetting ( " cre_fonts_recently_selected " )
-- Recreate it now, sorted alphabetically (we may not go visit
-- and refresh the font menu until quit, but we want to be able
-- to notice newly added fonts at next startup).
self : sortFaceList ( cre.getFontFaces ( ) )
self.face_table . needs_refresh = true
end ,
} )
end ,
2020-10-05 20:26:33 +02:00
separator = true ,
} )
2020-04-28 23:57:10 +02:00
table.insert ( settings_table , {
text = _ ( " Use additional fallback fonts " ) ,
checked_func = function ( )
return G_reader_settings : nilOrTrue ( " additional_fallback_fonts " )
end ,
callback = function ( )
2020-10-05 20:26:33 +02:00
G_reader_settings : flipNilOrTrue ( " additional_fallback_fonts " )
2020-04-28 23:57:10 +02:00
self.ui . document : setupFallbackFontFaces ( )
self.ui : handleEvent ( Event : new ( " UpdatePos " ) )
end ,
help_text = T ( _ ( [ [
Enable additional fallback fonts , for the most complete script and language coverage .
These fonts will be used in this order :
% 1
You can set a preferred fallback font with a long - press on a font name , and it will be used before these .
If that font happens to be part of this list already , it will be used first . ] ] ) ,
table.concat ( self.ui . document.fallback_fonts , " \n " ) ) ,
2022-07-18 01:16:51 +02:00
} )
table.insert ( settings_table , {
text = _ ( " Adjust fallback font sizes " ) ,
checked_func = function ( )
return G_reader_settings : nilOrTrue ( " cre_adjusted_fallback_font_sizes " )
end ,
callback = function ( )
G_reader_settings : flipNilOrTrue ( " cre_adjusted_fallback_font_sizes " )
self.ui . document : setAdjustedFallbackFontSizes ( G_reader_settings : nilOrTrue ( " cre_adjusted_fallback_font_sizes " ) )
self.ui : handleEvent ( Event : new ( " UpdatePos " ) )
end ,
help_text = _ ( [ [
Adjust the size of each fallback font so they all get the same x - height , and lowercase characters picked in them look similarly sized as those from the defaut font .
This may help with Greek words among Latin text ( as Latin fonts often do not have all the Greek characters ) , but may make Chinese or Indic characters smaller when picked from fallback fonts . ] ] ) ,
separator = true ,
} )
table.insert ( settings_table , {
text_func = function ( )
local scale = G_reader_settings : readSetting ( " cre_monospace_scaling " ) or 100
return T ( _ ( " Monospace fonts scaling: %1 % " ) , scale )
end ,
callback = function ( )
local SpinWidget = require ( " ui/widget/spinwidget " )
UIManager : show ( SpinWidget : new {
value = G_reader_settings : readSetting ( " cre_monospace_scaling " ) or 100 ,
value_min = 30 ,
value_step = 1 ,
value_hold_step = 5 ,
value_max = 150 ,
unit = " % " ,
title_text = _ ( " Monospace font scaling " ) ,
-- no info_text: we want this widget to stay small, so we can move it
-- around to see the effect of the scaling
keep_shown_on_apply = true ,
callback = function ( spin )
local scale = spin.value
G_reader_settings : saveSetting ( " cre_monospace_scaling " , scale )
self.ui . document : setMonospaceFontScaling ( scale )
self.ui : handleEvent ( Event : new ( " UpdatePos " ) )
2022-09-08 21:52:06 +02:00
end ,
2022-07-18 01:16:51 +02:00
} )
end ,
help_text = _ ( [ [
Monospace fonts may look big when inline with your main font if it has a small x - height .
This setting allows scaling all monospace fonts by this percentage so they can fit your preferred font height , or you can make them be a bit smaller to distinguish them more easily . ] ] ) ,
2020-04-28 23:57:10 +02:00
separator = true ,
} )
table.insert ( settings_table , {
text = _ ( " Generate font test document " ) ,
callback = function ( )
UIManager : show ( ConfirmBox : new {
2022-09-08 21:52:06 +02:00
text = _ ( " Would you like to generate an HTML document showing some sample text rendered with each available font? " ) ,
2020-04-28 23:57:10 +02:00
ok_callback = function ( )
self : buildFontsTestDocument ( )
2022-09-08 21:52:06 +02:00
end ,
2020-04-28 23:57:10 +02:00
} )
end ,
} )
return settings_table
2018-05-11 17:48:26 +02:00
end
2023-07-12 22:29:13 +02:00
function ReaderFont : addToRecentlySelectedList ( face )
local idx = util.arrayContains ( self.fonts_recently_selected , face )
if idx then
table.remove ( self.fonts_recently_selected , idx )
end
table.insert ( self.fonts_recently_selected , 1 , face )
if G_reader_settings : isTrue ( " font_menu_sort_by_recently_selected " ) then
self.face_table . needs_refresh = true
end
end
function ReaderFont : sortFaceList ( face_list )
self.fonts_recently_selected = G_reader_settings : readSetting ( " cre_fonts_recently_selected " )
if not self.fonts_recently_selected then
-- Init this list with the alphabetical list we got
self.fonts_recently_selected = face_list
G_reader_settings : saveSetting ( " cre_fonts_recently_selected " , self.fonts_recently_selected )
-- We got no list of previously known fonts, so we can't say which are new.
newly_added_fonts = { }
return face_list
end
if not newly_added_fonts then
-- First call after launch: check for fonts not yet known
newly_added_fonts = { }
local seen_fonts = { }
for _ , face in ipairs ( self.fonts_recently_selected ) do
seen_fonts [ face ] = false -- was there last time, might no longer be now
end
for _ , face in ipairs ( face_list ) do
if seen_fonts [ face ] == nil then -- not known
newly_added_fonts [ face ] = true
-- Add newly seen fonts at start of the recently set list,
-- so the user can see and test them more easily.
table.insert ( self.fonts_recently_selected , 1 , face )
end
seen_fonts [ face ] = true
end
-- Remove no-longer-there fonts from our list
util.arrayRemove ( self.fonts_recently_selected , function ( t , i , j )
return seen_fonts [ t [ i ] ]
end )
end
if G_reader_settings : isTrue ( " font_menu_sort_by_recently_selected " ) then
return self.fonts_recently_selected
end
-- Otherwise, return face_list as we got it, alphabetically (as sorted by crengine),
-- but still with newly added fonts first
if next ( newly_added_fonts ) then
local move_idx = 1
for i = 1 , # face_list do
if newly_added_fonts [ face_list [ i ] ] then
table.insert ( face_list , move_idx , table.remove ( face_list , i ) )
move_idx = move_idx + 1
end
end
end
return face_list
end
2020-04-28 23:57:10 +02:00
-- Default sample file
2020-09-12 10:47:35 +02:00
local FONT_TEST_DEFAULT_SAMPLE_PATH = " frontend/ui/elements/font-test-sample-default.template "
2020-04-28 23:57:10 +02:00
-- Users can set their own sample file, that will be used if found
local FONT_TEST_USER_SAMPLE_PATH = require ( " datastorage " ) : getSettingsDir ( ) .. " /font-test-sample.html "
-- This document will be generated in the home or default directory
local FONT_TEST_FINAL_FILENAME = " font-test.html "
2018-05-11 17:48:26 +02:00
function ReaderFont : buildFontsTestDocument ( )
2020-04-28 23:57:10 +02:00
local html_sample
local f = io.open ( FONT_TEST_USER_SAMPLE_PATH , " r " )
if f then
html_sample = f : read ( " *all " )
f : close ( )
end
if not html_sample then
f = io.open ( FONT_TEST_DEFAULT_SAMPLE_PATH , " r " )
if not f then return nil end
html_sample = f : read ( " *all " )
f : close ( )
end
2018-05-11 17:48:26 +02:00
local dir = G_reader_settings : readSetting ( " home_dir " )
2021-03-06 22:44:18 +01:00
or require ( " apps/filemanager/filemanagerutil " ) . getDefaultDir ( )
or " . "
2020-04-28 23:57:10 +02:00
local font_test_final_path = dir .. " / " .. FONT_TEST_FINAL_FILENAME
f = io.open ( font_test_final_path , " w " )
if not f then return end
-- Using <section><title>...</title></section> allows for a TOC to be built by crengine
2018-05-11 17:48:26 +02:00
f : write ( string.format ( [ [
< ? xml version = " 1.0 " encoding = " UTF-8 " ? >
< html >
< head >
< title >% s </ title >
2019-03-03 09:16:27 +01:00
< style >
2021-09-01 14:17:33 +02:00
h1 {
2019-03-03 09:16:27 +01:00
font - size : large ;
font - weight : bold ;
text - align : center ;
page - break - before : always ;
2021-09-01 14:17:33 +02:00
margin - top : 0 ;
2019-03-03 09:16:27 +01:00
margin - bottom : 0.5 em ;
}
a { color : black ; }
</ style >
2018-05-11 17:48:26 +02:00
</ head >
< body >
2021-09-01 14:17:33 +02:00
< h1 >% s </ h1 >
2018-05-11 17:48:26 +02:00
] ] , _ ( " Available fonts test document " ) , _ ( " AVAILABLE FONTS " ) ) )
local face_list = cre.getFontFaces ( )
f : write ( " <div style='margin: 2em'> \n " )
for _ , font_name in ipairs ( face_list ) do
local font_id = font_name : gsub ( " " , " _ " ) : gsub ( " ' " , " _ " )
f : write ( string.format ( " <div><a href='#%s'>%s</a></div> \n " , font_id , font_name ) )
end
f : write ( " </div> \n \n " )
for _ , font_name in ipairs ( face_list ) do
local font_id = font_name : gsub ( " " , " _ " ) : gsub ( " ' " , " _ " )
2021-09-01 14:17:33 +02:00
f : write ( string.format ( " <h1 id='%s'>%s</h1> \n " , font_id , font_name ) )
2018-05-11 17:48:26 +02:00
f : write ( string.format ( " <div style='font-family: %s'> \n " , font_name ) )
f : write ( html_sample )
f : write ( " \n </div> \n \n " )
end
f : write ( " </body></html> \n " )
f : close ( )
UIManager : show ( ConfirmBox : new {
2020-04-28 23:57:10 +02:00
text = T ( _ ( " Document created as: \n %1 \n \n Would you like to read it now? " ) , BD.filepath ( font_test_final_path ) ) ,
2018-05-11 17:48:26 +02:00
ok_callback = function ( )
UIManager : scheduleIn ( 1.0 , function ( )
2020-04-28 23:57:10 +02:00
self.ui : switchDocument ( font_test_final_path )
2018-05-11 17:48:26 +02:00
end )
end ,
} )
end
2013-10-18 22:38:07 +02:00
return ReaderFont