2
0
mirror of https://github.com/koreader/koreader synced 2024-10-31 21:20:20 +00:00
koreader/frontend/apps/reader/modules/readerhyphenation.lua
poire-z 0599c440cc [RTL UI] Bidi-wrap filenames, paths, urls, metadata
bidi.lua:
- Revert "Alias everything to Bidi.nowrap() when in LTR UI,
  as using LTR isolates seems uneeded when already LTR" (part
  of a628714f) which was a wrong assumption: we need proper
  wrappers for all things paths. Enhance some of these wrappers.
- Fix GetText RTL wrapping which was losing empty lines and
  trailing \n.

- Wrap all paths, directories, filenames in the code with
  these wrappers.
- Wrap all book metadata (title, authors...) with BD.auto(),
  as it helps fixing some edge cases (like open/close quotation
  marks which are not considered as bracket types by FriBiDi).
  (Needed some minor logic changes in CoverBrowser.)

- Tweak hyphenation menu text
- Update forgotten SortWidget for UI mirroring
- KoptConfig: update "justification" index for RTL re-ordering,
  following the recent addition of the page_gap_height option.
2020-01-04 01:34:46 +01:00

310 lines
14 KiB
Lua
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

local BD = require("ui/bidi")
local Device = require("device")
local Event = require("ui/event")
local InfoMessage = require("ui/widget/infomessage")
local InputContainer = require("ui/widget/container/inputcontainer")
local JSON = require("json")
local MultiConfirmBox = require("ui/widget/multiconfirmbox")
local UIManager = require("ui/uimanager")
local logger = require("logger")
local util = require("util")
local _ = require("gettext")
local C_ = _.pgettext
local T = require("ffi/util").template
local Screen = Device.screen
local ReaderHyphenation = InputContainer:new{
hyph_menu_title = _("Hyphenation"),
hyph_table = nil,
}
function ReaderHyphenation:init()
self.lang_table = {}
self.hyph_table = {}
self.hyph_algs_settings = {}
self.hyph_alg = cre.getSelectedHyphDict()
table.insert(self.hyph_table, {
text_func = function()
-- Note: with our callback, we either get hyph_left_hyphen_min and
-- hyph_right_hyphen_min both nil, or both defined.
if G_reader_settings:readSetting("hyph_left_hyphen_min") or
G_reader_settings:readSetting("hyph_right_hyphen_min") then
-- @translators to RTL language translators: %1/left is the min length of the start of a hyphenated word, %2/right is the min length of the end of a hyphenated word (note that there is yet no support for hyphenation with RTL languages, so this will mostly apply to LTR documents)
return T(_("Left/right minimal sizes: %1 - %2"),
G_reader_settings:readSetting("hyph_left_hyphen_min"),
G_reader_settings:readSetting("hyph_right_hyphen_min"))
end
return _("Left/right minimal sizes: language defaults")
end,
callback = function()
local DoubleSpinWidget = require("/ui/widget/doublespinwidget")
local hyph_settings = self.hyph_algs_settings[self.hyph_alg] or {}
local alg_left_hyphen_min = hyph_settings.left_hyphen_min
local alg_right_hyphen_min = hyph_settings.right_hyphen_min
local hyph_limits_widget = DoubleSpinWidget:new{
-- Min (1) and max (10) values are enforced by crengine
left_value = G_reader_settings:readSetting("hyph_left_hyphen_min") or alg_left_hyphen_min or 2,
left_min = 1,
left_max = 10,
right_value = G_reader_settings:readSetting("hyph_right_hyphen_min") or alg_right_hyphen_min or 2,
right_min = 1,
right_max = 10,
left_default = alg_left_hyphen_min or 2,
right_default = alg_right_hyphen_min or 2,
-- let room on the widget sides so we can see
-- the hyphenation changes happening
width = Screen:getWidth() * 0.6,
default_values = true,
default_text = _("Use language defaults"),
title_text = _("Hyphenation limits"),
info_text = _([[
Set minimum length before hyphenation occurs.
These settings will apply to all books with any hyphenation dictionary.
'Use language defaults' resets them.]]),
callback = function(left_hyphen_min, right_hyphen_min)
G_reader_settings:saveSetting("hyph_left_hyphen_min", left_hyphen_min)
G_reader_settings:saveSetting("hyph_right_hyphen_min", right_hyphen_min)
self.ui.document:setHyphLeftHyphenMin(G_reader_settings:readSetting("hyph_left_hyphen_min") or alg_left_hyphen_min)
self.ui.document:setHyphRightHyphenMin(G_reader_settings:readSetting("hyph_right_hyphen_min") or alg_right_hyphen_min)
self.ui.toc:onUpdateToc()
-- signal readerrolling to update pos in new height, and redraw page
self.ui:handleEvent(Event:new("UpdatePos"))
end
}
UIManager:show(hyph_limits_widget)
end,
enabled_func = function()
return self.hyph_alg ~= "@none"
end,
})
table.insert(self.hyph_table, {
text = _("Trust soft hyphens"),
callback = function()
G_reader_settings:flipNilOrFalse("hyph_trust_soft_hyphens")
self.ui.document:setTrustSoftHyphens(G_reader_settings:isTrue("hyph_trust_soft_hyphens"))
self.ui.toc:onUpdateToc()
self.ui:handleEvent(Event:new("UpdatePos"))
end,
checked_func = function()
-- for @none and @softhyphens, set the checkbox to reflect how
-- these trust soft hyphens, no matter what our setting is
if self.hyph_alg == "@none" then
return false
elseif self.hyph_alg == "@softhyphens" then
return true
else
return G_reader_settings:isTrue("hyph_trust_soft_hyphens")
end
end,
enabled_func = function()
return self.hyph_alg ~= "@none" and self.hyph_alg ~= "@softhyphens"
end,
separator = true,
})
local lang_data_file = assert(io.open("./data/hyph/languages.json"), "r")
local ok, lang_data = pcall(JSON.decode, lang_data_file:read("*all"))
if ok and lang_data then
for k,v in ipairs(lang_data) do
self.hyph_algs_settings[v.filename] = v -- just store full table
table.insert(self.hyph_table, {
text_func = function()
local text = v.name
if v.filename == G_reader_settings:readSetting("hyph_alg_default") then
text = text .. ""
end
if v.filename == G_reader_settings:readSetting("hyph_alg_fallback") then
text = text .. " <20>"
end
return text
end,
callback = function()
self.hyph_alg = v.filename
self.ui.doc_settings:saveSetting("hyph_alg", self.hyph_alg)
UIManager:show(InfoMessage:new{
text = T(_("Changed hyphenation to %1."), BD.wrap(v.name)),
})
self.ui.document:setHyphDictionary(v.filename)
-- Apply hyphenation sides limits
self.ui.document:setHyphLeftHyphenMin(G_reader_settings:readSetting("hyph_left_hyphen_min") or v.left_hyphen_min)
self.ui.document:setHyphRightHyphenMin(G_reader_settings:readSetting("hyph_right_hyphen_min") or v.right_hyphen_min)
self.ui.toc:onUpdateToc()
-- signal readerrolling to update pos in new height, and redraw page
self.ui:handleEvent(Event:new("UpdatePos"))
end,
hold_callback = function(touchmenu_instance)
UIManager:show(MultiConfirmBox:new{
-- No real need for a way to remove default one, we can just
-- toggle between setting a default OR a fallback (if a default
-- one is set, no fallback will ever be used - if a fallback one
-- is set, no default is wanted; so when we set one below, we
-- remove the other).
text = T( _("Would you like %1 to be used as the default (★) or fallback (<28>) hyphenation language?\n\nDefault will always take precedence while fallback will only be used if the language of the book can't be automatically determined."), BD.wrap(v.name)),
choice1_text = _("Default"),
choice1_callback = function()
G_reader_settings:saveSetting("hyph_alg_default", v.filename)
G_reader_settings:delSetting("hyph_alg_fallback")
if touchmenu_instance then touchmenu_instance:updateItems() end
end,
choice2_text = C_("Hyphenation", "Fallback"),
choice2_callback = function()
G_reader_settings:saveSetting("hyph_alg_fallback", v.filename)
G_reader_settings:delSetting("hyph_alg_default")
if touchmenu_instance then touchmenu_instance:updateItems() end
end,
})
end,
checked_func = function()
return v.filename == self.hyph_alg
end,
separator = v.separator,
})
self.lang_table[v.language] = v.filename
if v.aliases then
for i,alias in ipairs(v.aliases) do
self.lang_table[alias] = v.filename
end
end
end
end
self.ui.menu:registerToMainMenu(self)
end
function ReaderHyphenation:parseLanguageTag(lang_tag)
-- Parse an RFC 5646 language tag, like "en-US" or "en".
-- https://tools.ietf.org/html/rfc5646
-- We are only interested in the language and region parts.
local language = nil
local region = nil
for part in util.gsplit(lang_tag, "-", false) do
if not language then
language = string.lower(part)
elseif string.len(part) == 2 and not string.match(part, "[^%a]") then
region = string.upper(part)
end
end
return language, region
end
function ReaderHyphenation:getDictForLanguage(lang_tag)
-- EPUB language is an RFC 5646 language tag.
-- http://www.idpf.org/epub/301/spec/epub-publications.html#sec-opf-dclanguage
--
-- FB2 language is a two-letter language code
-- (which is also a valid RFC 5646 language tag).
-- http://fictionbook.org/index.php/%D0%AD%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82_lang (in Russian)
local language, region = self:parseLanguageTag(lang_tag)
if not language then
return
end
local dict
if region then
dict = self.lang_table[language .. '-' .. region]
end
if not dict then
dict = self.lang_table[language]
end
return dict
end
-- Setting the hyph algo before loading the document may save crengine
-- from re-doing some expensive work at render time (the hyph algo
-- is accounted in the nodeStyleHash, and would cause a mismatch if it is
-- different at render time from how it was at load time - "English US" by
-- default - causing a full re-init of the nodes styles.)
-- We will only re-set it on pre-render (only then, after loading, we
-- know the document language) if it's really needed: when no algo saved
-- in book settings, no default algo, and book has some language defined.
function ReaderHyphenation:onReadSettings(config)
-- Decide and set the adequate hyph algorithm according to settings
local hyph_alg
self.allow_doc_lang_hyph_alg_override = false
-- Use the one manually set for this document
hyph_alg = config:readSetting("hyph_alg")
if hyph_alg then
logger.dbg("Hyphenation: using", hyph_alg, "from doc settings")
self:setHyphAlgo(hyph_alg)
return
end
-- Use the one manually set as default (with Hold)
hyph_alg = G_reader_settings:readSetting("hyph_alg_default")
if hyph_alg then
logger.dbg("Hyphenation: using default ", hyph_alg)
self:setHyphAlgo(hyph_alg)
return
end
-- Document language will be allowed to override the one we set from now on
self.allow_doc_lang_hyph_alg_override = true
-- Use the one manually set as fallback (with Hold)
hyph_alg = G_reader_settings:readSetting("hyph_alg_fallback")
if hyph_alg then
logger.dbg("Hyphenation: using fallback ", hyph_alg, ", might be overriden by doc language")
self:setHyphAlgo(hyph_alg)
return
end
-- None decided, get back the current one set in crengine
logger.dbg("Hyphenation: no algo set")
self:setHyphAlgo()
logger.dbg("Hyphenation: keeping current crengine algo:", self.hyph_alg)
end
function ReaderHyphenation:onPreRenderDocument(config)
-- This is called after the document has been loaded
-- so we can access the document language.
local doc_language = self.ui.document:getProps().language
if not self.allow_doc_lang_hyph_alg_override then
logger.dbg("Hyphenation: not overriding", self.hyph_alg, "with doc language:",
(doc_language and doc_language or "none"))
elseif not doc_language then
logger.dbg("Hyphenation: no doc language, keeping", self.hyph_alg)
else
local hyph_alg = self:getDictForLanguage(doc_language)
if not hyph_alg then
logger.dbg("Hyphenation: no algo found for doc language:", doc_language, ", keeping", self.hyph_alg)
else
if hyph_alg == self.hyph_alg then
logger.dbg("Hyphenation: current", self.hyph_alg, "is right for doc language:", doc_language)
else
logger.dbg("Hyphenation: updating for doc language", doc_language, ":", self.hyph_alg, "=>", hyph_alg)
self:setHyphAlgo(hyph_alg)
end
end
end
end
function ReaderHyphenation:setHyphAlgo(hyph_alg)
if hyph_alg then
self.ui.document:setHyphDictionary(hyph_alg)
end
-- If we haven't set any (nil, or invalid), hardcoded
-- English_US.pattern (in cre.cpp) will be used
self.hyph_alg = cre.getSelectedHyphDict()
-- Apply hyphenation sides limits
local hyph_settings = self.hyph_algs_settings[self.hyph_alg] or {}
self.ui.document:setHyphLeftHyphenMin(G_reader_settings:readSetting("hyph_left_hyphen_min") or hyph_settings.left_hyphen_min)
self.ui.document:setHyphRightHyphenMin(G_reader_settings:readSetting("hyph_right_hyphen_min") or hyph_settings.right_hyphen_min)
self.ui.document:setTrustSoftHyphens(G_reader_settings:isTrue("hyph_trust_soft_hyphens"))
end
function ReaderHyphenation:addToMainMenu(menu_items)
-- insert table to main reader menu
menu_items.hyphenation = {
text = self.hyph_menu_title,
sub_item_table = self.hyph_table,
}
end
return ReaderHyphenation