koreader user patches

wednesday, 13 march 2024

koreader is a comprehensive ereader app. There is very little to want for it. The degree of end user control it provides is so vast, many who install KOReader are quickly overwhelmed by the plethora of menu options, only to return to their device’s much simpler and familiar native ereader platform. (i can be counted in as one myself, having installed and uninstalled it many times before affording it a deeper look).

Before KOReader, i merrily updated my kobopatches customizations with every Kobo Libra 2 firmware update—kudos to the devs who manage to roll out the updated kobopatches the same day the firmware updates roll out. KOReader changed all that because the margin, font and line spacing tweaks i made with kobopatches were unnecessary with KOReader.

If i could do nothing with KOReader, i would not feel my ereading experience lacking. It exceeds all the expectations one can have of an ereader application (certainly for me—there are many features i have yet to use. The screenshot below is a first time use)! But its LUA code foundation does allow one to look under the hood. So the inevitable happened..

lua changes

there were several items i wanted to tweak to further refine my personal ereading experience with KOReader..

  • Often times, the highlighted word for a dictionary lookup is hidden by the dictionary popup window. i wanted to increase the highlight delay so that after the popup clears, these senior eyes have sufficient time to relocate the highlighted word.

  • i like both my page turn buttons to page forward. This allows changing hand positions over the course of long reading sessions and using either button for advancing the page no matter which hand is being used or how the device is being held. The odd time i need to go back a page, i leave to tapping the screen.

  • i like to read at small font sizes with suitable line spacing for maximum readability, so needed to extend the ranges available to tweak the “proofing” layout i prefer to read with—from 12pt to 10pt for fonts and from 200% to 300% for line spacing.

  • And from a UI perspective, i wanted to apply my reading font to the statusbar and alt-statusbar for visual uniformity. i also prefer the statusbar separator to be spaces (versus glyphs for minimal visual noise) and wanted to control its width to clearly separate the information elements.

After poking around the code (with some help from the devs and others more intimate with the code) i was able to identify and modify the following source files..

feature lua file location
dictionary highlight
delay timer
dictquicklookup.lua frontend/ui/widget
page forward
(all buttons)
input.lua frontend/device
fontsize &
line spacing
creoptions.lua
readerfont.lua
frontend/ui/data
frontend/apps/reader/modules
statusbar font font.lua frontend/ui
alt-statusbar font credocument.lua frontend/document
statusbar separator readerfooter.lua frontend/apps/reader/modules

The bulk of the effort spent was actually consumed in locating the relevant source code files—tools like ack, ag (the silver searcher), etc. for doing string searches through folders of source code are your friend. The code changes themselves were trivial :)—the changes are more self-evident in the user patch explanations below.

Kobo screenshot

drift font book and statusbars

As before with kobopatches, i found myself updating the requisite source code files after each KOReader update. Updating KOReader was not as pressing since the application was already fulfilling everything i could have asked of it. But just to keep current and take advantage of any performance improvements..

It became quickly apparent that my customizations touched only stable areas of code which remained unchanged after subsequent updates (save for one syntax change in the latest update). Enter..

user patches

which provide a mechanism to override or add even, functionality to KOReader and are not overwritten by software updates. Anything that breaks is apparent at KOReader relaunch identifying the patch in question—i still check the updated sources and, so far, have come across only one syntactic change which i could have ignored.

The biggest challenge i found was identifying the lua “requires” modules (sources) that define the KOReader functions called within the patch. Most often, these references are easily pulled from the original source whose function is being modified. Sometimes, though, culling the source code base with a string search engine for a function or data element keyword is necessary to identify the possible candidates. (It gets easier with each additional user patch one is inspired to create)!

It is initially daunting, the code base being so large. But soon the branch of code relevant to one’s needs becomes apparent. In my case, with everything being UI related, all the changes resided in the “frontend” branch. The only head scratcher that tripped me up was a reference to a “settings.reader.lua” file. Search as i might on the KOReader git source, this file could not be found! Turned out, it is created on the device on launch. Doh!

Some patches merely required overriding a data element. KOReader would take care of the rest. Other patches required bundling the whole function that had a single hard coded setting i wished to override.

The following user patches may look complicated but very few statements are modified or added by me. Note the “requires” statments and the functions they reference. The numeric source code prefix designates triggering after the KOReader UIManager is ready—see the wiki.

2-dictionary-close-highlight-delay.lua

a simple copy of the original KOReader function with the hard coded delay increased from 0.5 seconds..

local Event = require("ui/event") local UIManager = require("ui/uimanager") local DictQuickLookup = require("ui/widget/dictquicklookup") DictQuickLookup.onClose = function(self) for menu, _ in pairs(self.menu_opened) do UIManager:close(menu) end self.menu_opened = {} UIManager:close(self) if self.update_wiki_languages_on_close then if not self.results.no_result then self.ui:handleEvent(Event:new(“UpdateWikiLanguages”, self.wiki_languages)) end end if self.highlight and not no_clear then local clear_id = self.highlight:getClearId() UIManager:scheduleIn(1.5, function() <– from 0.5 seconds delay self.highlight:clear(clear_id) end) end return true end

The patch may appear disproportionate to the single hard coded value that could otherwise be edited directly in the original source code. But it is a one time effort and well worth it IMO. It is the same for all the other user patches.

2-fontsize-linespacing.lua

two sources are referenced in this user patch wrapper. The first part sets the new fontsize and linespacing ranges..

local min_fontsize = 10 <-- point size local max_linespacing = 300 <-- percent local CreOptions = require("ui/data/creoptions") CreOptions[3].options[4].more_options_param.value_max = max_linespacing CreOptions[4].options[2].more_options_param.value_min = min_fontsize The second part updates the corresponding UI popups and their allowable input values to the new ranges.. local Screen = require("device").screen local Event = require("ui/event") local Notification = require("ui/widget/notification") local T = require("ffi/util").template local _ = require("gettext") local ReaderFont = require("apps/reader/modules/readerfont") function ReaderFont:onSetFontSize(size) size = math.max(min_fontsize, math.min(size, 255)) <– from 12pt min self.configurable.font_size = size self.ui.document:setFontSize(Screen:scaleBySize(size)) self.ui:handleEvent(Event:new(“UpdatePos”)) Notification:notify(T(_(“Font size set to: %1.”), size)) return true end function ReaderFont:onSetLineSpace(space) space = math.max(50, math.min(space, max_linespacing)) <– from 200% max self.configurable.line_spacing = space self.ui.document:setInterlineSpacePercent(space) self.ui:handleEvent(Event:new(“UpdatePos”)) Notification:notify(T(_(“Line spacing set to: %1%.”), space)) return true end

2-page-forward-buttons.lua

this user patch simply redefines the mapping table for button actions in their corresponding 360 degree portrait/landscape device orientations..

local logger = require("logger") local Device = require("device") Device.input.rotation_map = { [0] = { RPgBack = “RPgFwd” }, <– from {} [1] = { Up = “Right”, Right = “Down”, Down = “Left”, Left = “Up”, LPgBack = “LPgFwd”, LPgFwd = “LPgBack”, RPgBack = “RPgFwd”, RPgFwd = “RPgFwd” }, <– from RPgFwd = “RPgBack” [2] = { Up = “Down”, Right = “Left”, Down = “Up”, Left = “Right”, LPgFwd = “LPgBack”, LPgBack = “LPgFwd”, RPgFwd = “RPgFwd”, RPgBack = “RPgFwd” }, <– from RPgFwd = “RPgBack” [3] = { Up = “Left”, Right = “Up”, Down = “Right”, Left = “Down”, RPgBack = “RPgFwd” }, <– from default RPgBack = “RPgBack” } logger.info("Hardware button rotation disabled by user patch")

2-statusbar-font.lua

this patch sets the statusbar and alt-statusbar font to the document’s default font..

local DataStorage = require("datastorage") local G_reader_settings = require("luasettings"):open(DataStorage:getDataDir() .. "/settings.reader.lua") local fontface = G_reader_settings:readSetting("cre_font") The alt-statusbar requires just the font family name be set.. local CreDocument = require("document/credocument") CreDocument.header_font = fontface <-- alt-statusbar font The statusbar requires the corresponding font source and locates the matching filename from KOReader’s fonts folder.. local Font = require("ui/font") names = { "-book.ttf", "-book.otf", "-regular.ttf", "-regular.otf" } for _, n in pairs(names) do local f = io.open(DataStorage:getDataDir() .. “/fonts/” .. fontface .. n, “r”) if f ~= nil then <– font source exists io.close(f) local fontsource = fontface .. n <– statusbar font filename for k, _ in pairs(Font.fontmap) do if k == “ffont” then Font.fontmap[k] = fontsource elseif k == “smallffont” then Font.fontmap[k] = fontsource elseif k == “largeffont” then Font.fontmap[k] = fontsource end end break end end

NOTE: KOReader’s .sdr file for the opened document contains the current font settings for the document. The alt-statusbar font may require a reset of the document settings (from the top menu) to re-initialize the settings to the desired font.

2-statusbar-separator.lua

this final patch simply bypasses KOReader’s function and returns the desired separator, in this case, a single space—this pads KOReader’s default spacing for “no separator”. It could easily be modified to return any glyph string..

local ReaderFooter = require("apps/reader/modules/readerfooter") ReaderFooter.get_separator_symbol = function(self) return “ “ end

NOTE: This patch overrides any glyph choices made with the statusbar separator UI dialogue.

The result of all this..

Kindle Oasis with KOReader user patches

Examined closely, the statusbars reflect the document’s reading font. (i admit, one has to be, perhaps, more than a touch anal to object to a contrasting header/footer font.. :)

koreader portability

Is this effort worth it? i feel so. KOReader continues to be enhanced and updated regularly. But there is more!

On tha Kobo and jail broken Kindle, KOReader’s lua source code is accessible for direct modification—useful for testing before deciding whether or not to create a user patch. On Android devices, however, user patches are the only means for implementing such customizations, short of rebuilding the Android apk altogether.

With the exact same user patches in Android’s KOReader patches folder..

Android phone with KOReader user patches

Voila!

You might have noticed the wide left/right margins used on the Oasis. As a result of trying out a similar line width as might be expected on a candy bar shaped device (looking at you, Boox Palma) i went to a 50 character line width for acclimatization—at the lower end of the 45-75 character width spectrum for optimal reading consumption—and discovered i quite like that newspaper like column format (remember when news was leisurely consumed over a morning coffee and a folded paper?).

This opens the door to exploring reading on other platforms exactly the way i want. KOReader never ceases to impress. And i, for one, am truly indebted to those involved who have created this application for dedicated ereaders eveywhere.

repos

the above user patches may be found in my dotfiles under the koreader folder.

»»  adrift font

comment ?