<NME> readertypeset.lua
<BEF> local BD = require("ui/bidi")
local ConfirmBox = require("ui/widget/confirmbox")
local Event = require("ui/event")
local InfoMessage = require("ui/widget/infomessage")
local Math = require("optmath")
local Notification = require("ui/widget/notification")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local lfs = require("libs/libkoreader-lfs")
local optionsutil = require("ui/data/optionsutil")
local _ = require("gettext")
local C_ = _.pgettext
local Screen = require("device").screen
local T = require("ffi/util").template

local ReaderTypeset = WidgetContainer:extend{
    -- @translators This is style in the sense meant by CSS (cascading style sheets), relating to the layout and presentation of the document. See <https://en.wikipedia.org/wiki/CSS> for more information.
    css_menu_title = C_("CSS", "Style"),
end

function ReaderTypeset:onReadSettings(config)
    self.css = config:readSetting("css")
    if self.css then
        self.ui.document:setStyleSheet(self.css)
    else
end

function ReaderTypeset:onReadSettings(config)
    self.css = config:readSetting("css")
            or G_reader_settings:readSetting("copt_css")
            or self.ui.document.default_css
    local tweaks_css = self.ui.styletweak:getCssText()
    self.ui.document:setStyleSheet(self.css, tweaks_css)

    if config:has("embedded_fonts") then
        self.embedded_fonts = config:isTrue("embedded_fonts")
    else
        -- default to enable embedded fonts
        -- note that it's a bit confusing here:
        -- global settins store 0/1, while document settings store false/true
        -- we leave it that way for now to maintain backwards compatibility
        local global = G_reader_settings:readSetting("copt_embedded_fonts")
        self.embedded_fonts = (global == nil or global == 1) and true or false
    end
    -- As this is new, call it only when embedded_fonts are explicitely disabled
    -- self.ui.document:setEmbeddedFonts(self.embedded_fonts and 1 or 0)
    if not self.embedded_fonts then
        self.ui.document:setEmbeddedFonts(0)
    end

    if config:has("embedded_css") then
        self.embedded_css = config:isTrue("embedded_css")
    else
        -- default to enable embedded CSS
        -- note that it's a bit confusing here:
        -- global settings store 0/1, while document settings store false/true
        -- we leave it that way for now to maintain backwards compatibility
        local global = G_reader_settings:readSetting("copt_embedded_css")
        self.embedded_css = (global == nil or global == 1) and true or false
    end
    self.ui.document:setEmbeddedStyleSheet(self.embedded_css and 1 or 0)

    -- Block rendering mode: stay with legacy rendering for books
    -- previously opened so bookmarks and highlights stay valid.
end

function ReaderTypeset:genStyleSheetMenu()
    local file_list = {
        {
            text = _("clear all external styles"),
            callback = function()
                self:setStyleSheet("")
            end
        },
        {
            text = _("Auto"),
            callback = function()
                self:setStyleSheet(self.ui.document.default_css)
            end
        },
    }
    for f in lfs.dir("./data") do
        if lfs.attributes("./data/"..f, "mode") == "file" and string.match(f, "%.css$") then
            table.insert(file_list, {
                text = f,
                callback = function()
                    self:setStyleSheet("./data/"..f)
                end
            })
        end
    end
    return file_list
end

function ReaderTypeset:setStyleSheet(new_css)
    self.sync_t_b_page_margins = config:readSetting("copt_sync_t_b_page_margins")
                              or G_reader_settings:readSetting("copt_sync_t_b_page_margins")
                              or 0
    self.sync_t_b_page_margins = self.sync_t_b_page_margins == 1 and true or false

    -- default to disable TXT formatting as it does more harm than good
    self.txt_preformatted = config:readSetting("txt_preformatted")
                         or G_reader_settings:readSetting("txt_preformatted")
                         or 1
    self:toggleTxtPreFormatted(self.txt_preformatted)

    -- default to disable smooth scaling for now.
    if config:has("smooth_scaling") then
        self.smooth_scaling = config:isTrue("smooth_scaling")
    else
        local global = G_reader_settings:readSetting("copt_smooth_scaling")
        self.smooth_scaling = global == 1 and true or false
    end
    self:toggleImageScaling(self.smooth_scaling)

    -- default to automagic nightmode-friendly handling of images
    if config:has("nightmode_images") then
        self.nightmode_images = config:isTrue("nightmode_images")
    else
        local global = G_reader_settings:readSetting("copt_nightmode_images")
        self.nightmode_images = (global == nil or global == 1) and true or false
    end
    self:toggleNightmodeImages(self.nightmode_images)
end

function ReaderTypeset:onSaveSettings()
    self.ui.doc_settings:saveSetting("css", self.css)
    self.ui.doc_settings:saveSetting("embedded_css", self.embedded_css)
    self.ui.doc_settings:saveSetting("embedded_fonts", self.embedded_fonts)
    self.ui.doc_settings:saveSetting("render_dpi", self.render_dpi)
    self.ui.doc_settings:saveSetting("smooth_scaling", self.smooth_scaling)
    self.ui.doc_settings:saveSetting("nightmode_images", self.nightmode_images)
end

function ReaderTypeset:onToggleEmbeddedStyleSheet(toggle)
    self:toggleEmbeddedStyleSheet(toggle)
    if toggle then
        Notification:notify(_("Enabled embedded styles."))
    else
        Notification:notify(_("Disabled embedded styles."))
    end
    return true
end

function ReaderTypeset:onToggleEmbeddedFonts(toggle)
    self:toggleEmbeddedFonts(toggle)
    if toggle then
        Notification:notify(_("Enabled embedded fonts."))
    else
        Notification:notify(_("Disabled embedded fonts."))
    end
    return true
end

function ReaderTypeset:onToggleImageScaling(toggle)
    self:toggleImageScaling(toggle)
    Notification:notify(T( _("Image scaling set to: %1"), optionsutil:getOptionText("ToggleImageScaling", toggle)))
    return true
end

function ReaderTypeset:onToggleNightmodeImages(toggle)
    self:toggleNightmodeImages(toggle)
    return true
end

    })
end

function ReaderTypeset:onSetPageMargins(margins)
    local left = Screen:scaleBySize(margins[1])
    local top = Screen:scaleBySize(margins[2])
    "rtf.css",
    "txt.css",
}

function ReaderTypeset:onSetRenderDPI(dpi)
    self:setRenderDPI(dpi)
    Notification:notify(T( _("Zoom set to: %1"), optionsutil:getOptionText("SetRenderDPI", dpi)))
    return true
end

function ReaderTypeset:genStyleSheetMenu()
    local getStyleMenuItem = function(text, css_file, separator)
        return {
            text_func = function()
                return text .. (css_file == G_reader_settings:readSetting("copt_css") and "   ★" or "")
            end,
            callback = function()
                self:setStyleSheet(css_file or self.ui.document.default_css)
            end,
            hold_callback = function(touchmenu_instance)
                self:makeDefaultStyleSheet(css_file, text, touchmenu_instance)
            end,
            checked_func = function()
                if not css_file then -- "Auto"
                    return self.css == self.ui.document.default_css
                end
                return css_file == self.css
            end,
            separator = separator,
        }
    end

    local style_table = {}
    local obsoleted_table = {}

    table.insert(style_table, getStyleMenuItem(_("None"), ""))
    table.insert(style_table, getStyleMenuItem(_("Auto"), nil, true))

    local css_files = {}
    for f in lfs.dir("./data") do
        if lfs.attributes("./data/"..f, "mode") == "file" and string.match(f, "%.css$") then
            css_files[f] = "./data/"..f
        end
    end
    -- Add the 3 main styles
    if css_files["epub.css"] then
        table.insert(style_table, getStyleMenuItem(_("HTML / EPUB (epub.css)"), css_files["epub.css"]))
        css_files["epub.css"] = nil
    end
    if css_files["html5.css"] then
        table.insert(style_table, getStyleMenuItem(_("HTML5 (html5.css)"), css_files["html5.css"]))
        css_files["html5.css"] = nil
    end
    if css_files["fb2.css"] then
        table.insert(style_table, getStyleMenuItem(_("FictionBook (fb2.css)"), css_files["fb2.css"], true))
        css_files["fb2.css"] = nil
    end
    -- Add the obsoleted ones to the Obsolete sub menu
    local obsoleted_css = {} -- for check_func of the Obsolete sub menu itself
    for __, css in ipairs(OBSOLETED_CSS) do
        obsoleted_css[css_files[css]] = css
        if css_files[css] then
            table.insert(obsoleted_table, getStyleMenuItem(css, css_files[css]))
            css_files[css] = nil
        end
    end
    -- Sort and add the remaining (user added) files if any
    local user_files = {}
    for css, css_file in pairs(css_files) do
        table.insert(user_files, css)
    end
    table.sort(user_files)
    for __, css in ipairs(user_files) do
        table.insert(style_table, getStyleMenuItem(css, css_files[css]))
    end

    style_table[#style_table].separator = true
    table.insert(style_table, {
        text_func = function()
            local text = _("Obsolete")
            if obsoleted_css[self.css] then
                text = T(_("Obsolete (%1)"), BD.filename(obsoleted_css[self.css]))
            end
            if obsoleted_css[G_reader_settings:readSetting("copt_css")] then
                text = text .. "   ★"
            end
            return text
        end,
        sub_item_table = obsoleted_table,
        checked_func = function()
            return obsoleted_css[self.css] ~= nil
        end
    })
    return style_table
end

function ReaderTypeset:onApplyStyleSheet()
    local tweaks_css = self.ui.styletweak:getCssText()
    self.ui.document:setStyleSheet(self.css, tweaks_css)
    self.ui:handleEvent(Event:new("UpdatePos"))
    return true
end

function ReaderTypeset:setStyleSheet(new_css)
    if new_css ~= self.css then
        self.css = new_css
        local tweaks_css = self.ui.styletweak:getCssText()
        self.ui.document:setStyleSheet(new_css, tweaks_css)
        self.ui:handleEvent(Event:new("UpdatePos"))
    end
end

function ReaderTypeset:setEmbededStyleSheetOnly()
    if self.css ~= nil then
        -- clear applied css
        self.ui.document:setStyleSheet("")
        self.ui.document:setEmbeddedStyleSheet(1)
        self.css = nil
        self.ui:handleEvent(Event:new("UpdatePos"))
    end
end

function ReaderTypeset:toggleEmbeddedStyleSheet(toggle)
    if not toggle then
        self.embedded_css = false
        self:setStyleSheet(self.ui.document.default_css)
        self.ui.document:setEmbeddedStyleSheet(0)
    else
        self.embedded_css = true
        --self:setStyleSheet(self.ui.document.default_css)
        self.ui.document:setEmbeddedStyleSheet(1)
    end
    self.ui:handleEvent(Event:new("UpdatePos"))
end

function ReaderTypeset:toggleEmbeddedFonts(toggle)
    if not toggle then
        self.embedded_fonts = false
        self.ui.document:setEmbeddedFonts(0)
    else
        self.embedded_fonts = true
        self.ui.document:setEmbeddedFonts(1)
    end
    self.ui:handleEvent(Event:new("UpdatePos"))
end

-- crengine enhanced block rendering feature/flags (see crengine/include/lvrend.h):
--                                               legacy flat book web
-- ENHANCED                           0x00000001          x    x   x
-- ALLOW_PAGE_BREAK_WHEN_NO_CONTENT   0x00000002                   x
--
-- COLLAPSE_VERTICAL_MARGINS          0x00000010          x    x   x
-- ALLOW_VERTICAL_NEGATIVE_MARGINS    0x00000020          x    x   x
-- ALLOW_NEGATIVE_COLLAPSED_MARGINS   0x00000040                   x
--
-- ENSURE_MARGIN_AUTO_ALIGNMENT       0x00000100               x   x
-- ALLOW_HORIZONTAL_NEGATIVE_MARGINS  0x00000200                   x
-- ALLOW_HORIZONTAL_BLOCK_OVERFLOW    0x00000400                   x
-- ALLOW_HORIZONTAL_PAGE_OVERFLOW     0x00000800                   x
--
-- USE_W3C_BOX_MODEL                  0x00001000          x    x   x
-- ALLOW_STYLE_W_H_ABSOLUTE_UNITS     0x00002000                   x
-- ENSURE_STYLE_WIDTH                 0x00004000               x   x
-- ENSURE_STYLE_HEIGHT                0x00008000                   x
--
-- WRAP_FLOATS                        0x00010000          x    x   x
-- PREPARE_FLOATBOXES                 0x00020000          x    x   x
-- FLOAT_FLOATBOXES                   0x00040000               x   x
-- DO_NOT_CLEAR_OWN_FLOATS            0x00100000               x   x
-- ALLOW_EXACT_FLOATS_FOOTPRINTS      0x00200000               x   x
--
-- BOX_INLINE_BLOCKS                  0x01000000          x    x   x
-- COMPLETE_INCOMPLETE_TABLES         0x02000000          x    x   x

local BLOCK_RENDERING_FLAGS = {
    0x00000000, -- legacy block rendering
    0x03030031, -- flat mode (with prepared floatBoxes, so inlined, to avoid display hash mismatch)
    0x03375131, -- book mode (floating floatBoxes, limited widths support)
    0x7FFFFFFF, -- web mode, all features/flags
}

function ReaderTypeset:setBlockRenderingMode(mode)
    -- mode starts for now with 0 = legacy, so we may later be able
    -- to remove it and then start with 1 = flat
    -- (Ensure we don't crash if we added and removed some options)
    if mode + 1 > #BLOCK_RENDERING_FLAGS then
        mode = #BLOCK_RENDERING_FLAGS - 1
    end
    local flags = BLOCK_RENDERING_FLAGS[mode + 1]
    if not flags then
        return
    end
    self.block_rendering_mode = mode
    if self.ensure_saner_block_rendering_flags then -- see next function
        -- Don't enable BOX_INLINE_BLOCKS
        -- inlineBoxes have been around and allowed on older DOM_VERSION
        -- for a few weeks - let's disable it: it may break highlights
        -- made during this time, but may resurrect others made during
        -- a longer previous period of time.
        flags = bit.band(flags, bit.bnot(0x01000000))
        -- Don't enable COMPLETE_INCOMPLETE_TABLES, as they may add
        -- many boxing elements around huge amount of text, and break
        -- some past highlights made on the non-boxed elements.
        flags = bit.band(flags, bit.bnot(0x02000000))
    end
    self.ui.document:setBlockRenderingFlags(flags)
    self.ui:handleEvent(Event:new("UpdatePos"))
end

function ReaderTypeset:ensureSanerBlockRenderingFlags(mode)
    -- Called by ReaderRolling:onReadSettings() when old
    -- DOM version requested, before normalized xpointers,
    -- asking us to unset some of the flags set previously.
    self.ensure_saner_block_rendering_flags = true
    self:setBlockRenderingMode(self.block_rendering_mode)
end

function ReaderTypeset:toggleImageScaling(toggle)
    if toggle and (toggle == true or toggle == 1) then
        self.smooth_scaling = true
        self.ui.document:setImageScaling(true)
    else
        self.smooth_scaling = false
        self.ui.document:setImageScaling(false)
    end
    self.ui:handleEvent(Event:new("UpdatePos"))
end

function ReaderTypeset:toggleNightmodeImages(toggle)
    if toggle and (toggle == true or toggle == 1) then
        self.nightmode_images = true
        self.ui.document:setNightmodeImages(true)
    else
        self.nightmode_images = false
        self.ui.document:setNightmodeImages(false)
    end
    self.ui:handleEvent(Event:new("UpdatePos"))
end

function ReaderTypeset:toggleTxtPreFormatted(toggle)
    self.ui.document:setTxtPreFormatted(toggle)
    self.ui:handleEvent(Event:new("UpdatePos"))
end

function ReaderTypeset:setRenderDPI(dpi)
    self.render_dpi = dpi
    self.ui.document:setRenderDPI(dpi)
    self.ui:handleEvent(Event:new("UpdatePos"))
end

function ReaderTypeset:addToMainMenu(menu_items)
    -- insert table to main reader menu
    menu_items.set_render_style = {
        text = self.css_menu_title,
        sub_item_table = self:genStyleSheetMenu(),
    }
end

function ReaderTypeset:makeDefaultStyleSheet(css, text, touchmenu_instance)
    UIManager:show(ConfirmBox:new{
        text = T( _("Set default style to %1?"), BD.filename(text)),
        ok_callback = function()
            G_reader_settings:saveSetting("copt_css", css)
            if touchmenu_instance then touchmenu_instance:updateItems() end
        end,
    })
end

function ReaderTypeset:onSetPageHorizMargins(h_margins, when_applied_callback)
    self.unscaled_margins = { h_margins[1], self.unscaled_margins[2], h_margins[2], self.unscaled_margins[4] }
    self.ui:handleEvent(Event:new("SetPageMargins", self.unscaled_margins, when_applied_callback))
end

function ReaderTypeset:onSetPageTopMargin(t_margin, when_applied_callback)
    self.unscaled_margins = { self.unscaled_margins[1], t_margin, self.unscaled_margins[3], self.unscaled_margins[4] }
    if self.sync_t_b_page_margins then
        self.unscaled_margins[4] = t_margin
        -- Let ConfigDialog know so it can update it on screen and have it saved on quit
        self.ui.document.configurable.b_page_margin = t_margin
    end
    self.ui:handleEvent(Event:new("SetPageMargins", self.unscaled_margins, when_applied_callback))
end

function ReaderTypeset:onSetPageBottomMargin(b_margin, when_applied_callback)
    self.unscaled_margins = { self.unscaled_margins[1], self.unscaled_margins[2], self.unscaled_margins[3], b_margin }
    if self.sync_t_b_page_margins then
        self.unscaled_margins[2] = b_margin
        -- Let ConfigDialog know so it can update it on screen and have it saved on quit
        self.ui.document.configurable.t_page_margin = b_margin
    end
    self.ui:handleEvent(Event:new("SetPageMargins", self.unscaled_margins, when_applied_callback))
end

function ReaderTypeset:onSetPageTopAndBottomMargin(t_b_margins, when_applied_callback)
    local t_margin, b_margin = t_b_margins[1], t_b_margins[2]
    self.unscaled_margins = { self.unscaled_margins[1], t_margin, self.unscaled_margins[3], b_margin }
    if t_margin ~= b_margin then
        -- Set Sync T/B Margins toggle to off, as user explicitly made them differ
        self.sync_t_b_page_margins = false
        self.ui.document.configurable.sync_t_b_page_margins = 0
    end
    self.ui:handleEvent(Event:new("SetPageMargins", self.unscaled_margins, when_applied_callback))
end

function ReaderTypeset:onSyncPageTopBottomMargins(toggle, when_applied_callback)
    self.sync_t_b_page_margins = not self.sync_t_b_page_margins
    if self.sync_t_b_page_margins then
        -- Adjust current top and bottom margins if needed
        if self.unscaled_margins[2] ~= self.unscaled_margins[4] then
            -- Taking the rounded mean can change the vertical page height,
            -- and so the previous lines layout. We could have used the mean
            -- for the top, and the delta from the mean for the bottom (and
            -- have them possibly not equal), but as these are unscaled here,
            -- and later scaled, the end result could still be different.
            -- So just take the mean and make them equal.
            local mean_margin = Math.round((self.unscaled_margins[2] + self.unscaled_margins[4]) / 2)
            self.ui.document.configurable.t_page_margin = mean_margin
            self.ui.document.configurable.b_page_margin = mean_margin
            self.unscaled_margins = { self.unscaled_margins[1], mean_margin, self.unscaled_margins[3], mean_margin }
            self.ui:handleEvent(Event:new("SetPageMargins", self.unscaled_margins, when_applied_callback))
            when_applied_callback = nil
        end
    end
    if when_applied_callback then
        when_applied_callback()
    end
end

function ReaderTypeset:onSetPageMargins(margins, when_applied_callback)
    local left = Screen:scaleBySize(margins[1])
    local top = Screen:scaleBySize(margins[2])
    local right = Screen:scaleBySize(margins[3])
    local bottom
    if self.view.footer.reclaim_height then
        bottom = Screen:scaleBySize(margins[4])
    else
        bottom = Screen:scaleBySize(margins[4]) + self.view.footer:getHeight()
    end
    self.ui.document:setPageMargins(left, top, right, bottom)
    self.ui:handleEvent(Event:new("UpdatePos"))
    if when_applied_callback then
        -- Provided when hide_on_apply, and ConfigDialog temporarily hidden:
        -- show an InfoMessage with the unscaled & scaled values,
        -- and call when_applied_callback on dismiss
        UIManager:show(InfoMessage:new{
            text = T(_([[
Margins set to:

  left: %1 (%2px)
  right: %3 (%4px)
  top: %5 (%6px)
  bottom: %7 (%8px)

Tap to dismiss.]]),
            margins[1], left, margins[3], right, margins[2], top, margins[4], bottom),
            dismiss_callback = when_applied_callback,
        })
    end
end

return ReaderTypeset

<MSG> Implement persistent global style setting

<DFF> @@ -19,7 +19,7 @@ function ReaderTypeset:init()
 end
 
 function ReaderTypeset:onReadSettings(config)
-    self.css = config:readSetting("css")
+    self.css = config:readSetting("css") or G_reader_settings:readSetting("copt_css")
     if self.css then
         self.ui.document:setStyleSheet(self.css)
     else
@@ -65,31 +65,37 @@ function ReaderTypeset:onToggleEmbeddedStyleSheet(toggle)
 end
 
 function ReaderTypeset:genStyleSheetMenu()
+    local style_table = {}
     local file_list = {
         {
             text = _("clear all external styles"),
-            callback = function()
-                self:setStyleSheet("")
-            end
+            css = ""
         },
         {
             text = _("Auto"),
-            callback = function()
-                self:setStyleSheet(self.ui.document.default_css)
-            end
+            css = self.ui.document.default_css
         },
     }
     for f in lfs.dir("./data") do
         if lfs.attributes("./data/"..f, "mode") == "file" and string.match(f, "%.css$") then
             table.insert(file_list, {
                 text = f,
-                callback = function()
-                    self:setStyleSheet("./data/"..f)
-                end
+                css = "./data/"..f
             })
         end
     end
-    return file_list
+    for i,file in ipairs(file_list) do
+        table.insert(style_table, {
+            text = file["text"],
+            callback = function()
+                self:setStyleSheet(file["css"])
+            end,
+            hold_callback = function()
+                self:makeDefaultStyleSheet(file["css"], file["text"])
+            end
+        })
+    end
+    return style_table
 end
 
 function ReaderTypeset:setStyleSheet(new_css)
@@ -166,6 +172,18 @@ function ReaderTypeset:makeDefaultFloatingPunctuation()
     })
 end
 
+function ReaderTypeset:makeDefaultStyleSheet(css, text)
+    text = text or css
+    if css then
+        UIManager:show(ConfirmBox:new{
+            text = T( _("Set default style to %1?"), text),
+            ok_callback = function()
+                G_reader_settings:saveSetting("copt_css", css)
+            end,
+        })
+    end
+end
+
 function ReaderTypeset:onSetPageMargins(margins)
     local left = Screen:scaleBySize(margins[1])
     local top = Screen:scaleBySize(margins[2])

{'commit': 'diff --git a/frontend/apps/reader/modules/readertypeset.lua b/frontend/apps/reader/modules/readertypeset.lua\nindex 124b8e9b..cc3c77c2 100644\n--- a/frontend/apps/reader/modules/readertypeset.lua\n+++ b/frontend/apps/reader/modules/readertypeset.lua\n@@ -19,7 +19,7 @@ function ReaderTypeset:init()\n end\n \n function ReaderTypeset:onReadSettings(config)\n-    self.css = config:readSetting("css")\n+    self.css = config:readSetting("css") or G_reader_settings:readSetting("copt_css")\n     if self.css then\n         self.ui.document:setStyleSheet(self.css)\n     else\n@@ -65,31 +65,37 @@ function ReaderTypeset:onToggleEmbeddedStyleSheet(toggle)\n end\n \n function ReaderTypeset:genStyleSheetMenu()\n+    local style_table = {}\n     local file_list = {\n         {\n             text = _("clear all external styles"),\n-            callback = function()\n-                self:setStyleSheet("")\n-            end\n+            css = ""\n         },\n         {\n             text = _("Auto"),\n-            callback = function()\n-                self:setStyleSheet(self.ui.document.default_css)\n-            end\n+            css = self.ui.document.default_css\n         },\n     }\n     for f in lfs.dir("./data") do\n         if lfs.attributes("./data/"..f, "mode") == "file" and string.match(f, "%.css$") then\n             table.insert(file_list, {\n                 text = f,\n-                callback = function()\n-                    self:setStyleSheet("./data/"..f)\n-                end\n+                css = "./data/"..f\n             })\n         end\n     end\n-    return file_list\n+    for i,file in ipairs(file_list) do\n+        table.insert(style_table, {\n+            text = file["text"],\n+            callback = function()\n+                self:setStyleSheet(file["css"])\n+            end,\n+            hold_callback = function()\n+                self:makeDefaultStyleSheet(file["css"], file["text"])\n+            end\n+        })\n+    end\n+    return style_table\n end\n \n function ReaderTypeset:setStyleSheet(new_css)\n@@ -166,6 +172,18 @@ function ReaderTypeset:makeDefaultFloatingPunctuation()\n     })\n end\n \n+function ReaderTypeset:makeDefaultStyleSheet(css, text)\n+    text = text or css\n+    if css then\n+        UIManager:show(ConfirmBox:new{\n+            text = T( _("Set default style to %1?"), text),\n+            ok_callback = function()\n+                G_reader_settings:saveSetting("copt_css", css)\n+            end,\n+        })\n+    end\n+end\n+\n function ReaderTypeset:onSetPageMargins(margins)\n     local left = Screen:scaleBySize(margins[1])\n     local top = Screen:scaleBySize(margins[2])\n', 'commit_subject': 'Implement persistent global style setting', 'repo_name': 'koreader/koreader', 'committer': {'name': 'Frans de Jonge', 'email': '807d67be87a9aeacd1bff8b3eb885eb0f5b378c8@gmail.com', 'time_sec': '1440498332', 'tz_offset': '120', 'date': {'seconds': '1440498332'}}, 'commit_tree': '4856f5ef8866cfc68d9164dc43b4ee0ef8e91d7e', 'commit_message': 'Implement persistent global style setting\n', 'language_name': 'Python', 'license': 'agpl-3.0', 'hunks': '@@ -19,7 +19,7 @@ function ReaderTypeset:init()\n end\n \n function ReaderTypeset:onReadSettings(config)\n-    self.css = config:readSetting("css")\n+    self.css = config:readSetting("css") or G_reader_settings:readSetting("copt_css")\n     if self.css then\n         self.ui.document:setStyleSheet(self.css)\n     else\n@@ -65,31 +65,37 @@ function ReaderTypeset:onToggleEmbeddedStyleSheet(toggle)\n end\n \n function ReaderTypeset:genStyleSheetMenu()\n+    local style_table = {}\n     local file_list = {\n         {\n             text = _("clear all external styles"),\n-            callback = function()\n-                self:setStyleSheet("")\n-            end\n+            css = ""\n         },\n         {\n             text = _("Auto"),\n-            callback = function()\n-                self:setStyleSheet(self.ui.document.default_css)\n-            end\n+            css = self.ui.document.default_css\n         },\n     }\n     for f in lfs.dir("./data") do\n         if lfs.attributes("./data/"..f, "mode") == "file" and string.match(f, "%.css$") then\n             table.insert(file_list, {\n                 text = f,\n-                callback = function()\n-                    self:setStyleSheet("./data/"..f)\n-                end\n+                css = "./data/"..f\n             })\n         end\n     end\n-    return file_list\n+    for i,file in ipairs(file_list) do\n+        table.insert(style_table, {\n+            text = file["text"],\n+            callback = function()\n+                self:setStyleSheet(file["css"])\n+            end,\n+            hold_callback = function()\n+                self:makeDefaultStyleSheet(file["css"], file["text"])\n+            end\n+        })\n+    end\n+    return style_table\n end\n \n function ReaderTypeset:setStyleSheet(new_css)\n@@ -166,6 +172,18 @@ function ReaderTypeset:makeDefaultFloatingPunctuation()\n     })\n end\n \n+function ReaderTypeset:makeDefaultStyleSheet(css, text)\n+    text = text or css\n+    if css then\n+        UIManager:show(ConfirmBox:new{\n+            text = T( _("Set default style to %1?"), text),\n+            ok_callback = function()\n+                G_reader_settings:saveSetting("copt_css", css)\n+            end,\n+        })\n+    end\n+end\n+\n function ReaderTypeset:onSetPageMargins(margins)\n     local left = Screen:scaleBySize(margins[1])\n     local top = Screen:scaleBySize(margins[2])\n', 'addition_count': 29, 'deletion_count': 11, 'src_file': 'a/frontend/apps/reader/modules/readertypeset.lua', 'tgt_file': 'b/frontend/apps/reader/modules/readertypeset.lua', 'file_extension': '.lua', 'before_file': 'local BD = require("ui/bidi")\nlocal ConfirmBox = require("ui/widget/confirmbox")\nlocal Event = require("ui/event")\nlocal InfoMessage = require("ui/widget/infomessage")\nlocal Math = require("optmath")\nlocal Notification = require("ui/widget/notification")\nlocal UIManager = require("ui/uimanager")\nlocal WidgetContainer = require("ui/widget/container/widgetcontainer")\nlocal lfs = require("libs/libkoreader-lfs")\nlocal optionsutil = require("ui/data/optionsutil")\nlocal _ = require("gettext")\nlocal C_ = _.pgettext\nlocal Screen = require("device").screen\nlocal T = require("ffi/util").template\n\nlocal ReaderTypeset = WidgetContainer:extend{\n    -- @translators This is style in the sense meant by CSS (cascading style sheets), relating to the layout and presentation of the document. See <https://en.wikipedia.org/wiki/CSS> for more information.\n    css_menu_title = C_("CSS", "Style"),\nend\n\nfunction ReaderTypeset:onReadSettings(config)\n    self.css = config:readSetting("css")\n    if self.css then\n        self.ui.document:setStyleSheet(self.css)\n    else\nend\n\nfunction ReaderTypeset:onReadSettings(config)\n    self.css = config:readSetting("css")\n            or G_reader_settings:readSetting("copt_css")\n            or self.ui.document.default_css\n    local tweaks_css = self.ui.styletweak:getCssText()\n    self.ui.document:setStyleSheet(self.css, tweaks_css)\n\n    if config:has("embedded_fonts") then\n        self.embedded_fonts = config:isTrue("embedded_fonts")\n    else\n        -- default to enable embedded fonts\n        -- note that it\'s a bit confusing here:\n        -- global settins store 0/1, while document settings store false/true\n        -- we leave it that way for now to maintain backwards compatibility\n        local global = G_reader_settings:readSetting("copt_embedded_fonts")\n        self.embedded_fonts = (global == nil or global == 1) and true or false\n    end\n    -- As this is new, call it only when embedded_fonts are explicitely disabled\n    -- self.ui.document:setEmbeddedFonts(self.embedded_fonts and 1 or 0)\n    if not self.embedded_fonts then\n        self.ui.document:setEmbeddedFonts(0)\n    end\n\n    if config:has("embedded_css") then\n        self.embedded_css = config:isTrue("embedded_css")\n    else\n        -- default to enable embedded CSS\n        -- note that it\'s a bit confusing here:\n        -- global settings store 0/1, while document settings store false/true\n        -- we leave it that way for now to maintain backwards compatibility\n        local global = G_reader_settings:readSetting("copt_embedded_css")\n        self.embedded_css = (global == nil or global == 1) and true or false\n    end\n    self.ui.document:setEmbeddedStyleSheet(self.embedded_css and 1 or 0)\n\n    -- Block rendering mode: stay with legacy rendering for books\n    -- previously opened so bookmarks and highlights stay valid.\nend\n\nfunction ReaderTypeset:genStyleSheetMenu()\n    local file_list = {\n        {\n            text = _("clear all external styles"),\n            callback = function()\n                self:setStyleSheet("")\n            end\n        },\n        {\n            text = _("Auto"),\n            callback = function()\n                self:setStyleSheet(self.ui.document.default_css)\n            end\n        },\n    }\n    for f in lfs.dir("./data") do\n        if lfs.attributes("./data/"..f, "mode") == "file" and string.match(f, "%.css$") then\n            table.insert(file_list, {\n                text = f,\n                callback = function()\n                    self:setStyleSheet("./data/"..f)\n                end\n            })\n        end\n    end\n    return file_list\nend\n\nfunction ReaderTypeset:setStyleSheet(new_css)\n    self.sync_t_b_page_margins = config:readSetting("copt_sync_t_b_page_margins")\n                              or G_reader_settings:readSetting("copt_sync_t_b_page_margins")\n                              or 0\n    self.sync_t_b_page_margins = self.sync_t_b_page_margins == 1 and true or false\n\n    -- default to disable TXT formatting as it does more harm than good\n    self.txt_preformatted = config:readSetting("txt_preformatted")\n                         or G_reader_settings:readSetting("txt_preformatted")\n                         or 1\n    self:toggleTxtPreFormatted(self.txt_preformatted)\n\n    -- default to disable smooth scaling for now.\n    if config:has("smooth_scaling") then\n        self.smooth_scaling = config:isTrue("smooth_scaling")\n    else\n        local global = G_reader_settings:readSetting("copt_smooth_scaling")\n        self.smooth_scaling = global == 1 and true or false\n    end\n    self:toggleImageScaling(self.smooth_scaling)\n\n    -- default to automagic nightmode-friendly handling of images\n    if config:has("nightmode_images") then\n        self.nightmode_images = config:isTrue("nightmode_images")\n    else\n        local global = G_reader_settings:readSetting("copt_nightmode_images")\n        self.nightmode_images = (global == nil or global == 1) and true or false\n    end\n    self:toggleNightmodeImages(self.nightmode_images)\nend\n\nfunction ReaderTypeset:onSaveSettings()\n    self.ui.doc_settings:saveSetting("css", self.css)\n    self.ui.doc_settings:saveSetting("embedded_css", self.embedded_css)\n    self.ui.doc_settings:saveSetting("embedded_fonts", self.embedded_fonts)\n    self.ui.doc_settings:saveSetting("render_dpi", self.render_dpi)\n    self.ui.doc_settings:saveSetting("smooth_scaling", self.smooth_scaling)\n    self.ui.doc_settings:saveSetting("nightmode_images", self.nightmode_images)\nend\n\nfunction ReaderTypeset:onToggleEmbeddedStyleSheet(toggle)\n    self:toggleEmbeddedStyleSheet(toggle)\n    if toggle then\n        Notification:notify(_("Enabled embedded styles."))\n    else\n        Notification:notify(_("Disabled embedded styles."))\n    end\n    return true\nend\n\nfunction ReaderTypeset:onToggleEmbeddedFonts(toggle)\n    self:toggleEmbeddedFonts(toggle)\n    if toggle then\n        Notification:notify(_("Enabled embedded fonts."))\n    else\n        Notification:notify(_("Disabled embedded fonts."))\n    end\n    return true\nend\n\nfunction ReaderTypeset:onToggleImageScaling(toggle)\n    self:toggleImageScaling(toggle)\n    Notification:notify(T( _("Image scaling set to: %1"), optionsutil:getOptionText("ToggleImageScaling", toggle)))\n    return true\nend\n\nfunction ReaderTypeset:onToggleNightmodeImages(toggle)\n    self:toggleNightmodeImages(toggle)\n    return true\nend\n\n    })\nend\n\nfunction ReaderTypeset:onSetPageMargins(margins)\n    local left = Screen:scaleBySize(margins[1])\n    local top = Screen:scaleBySize(margins[2])\n    "rtf.css",\n    "txt.css",\n}\n\nfunction ReaderTypeset:onSetRenderDPI(dpi)\n    self:setRenderDPI(dpi)\n    Notification:notify(T( _("Zoom set to: %1"), optionsutil:getOptionText("SetRenderDPI", dpi)))\n    return true\nend\n\nfunction ReaderTypeset:genStyleSheetMenu()\n    local getStyleMenuItem = function(text, css_file, separator)\n        return {\n            text_func = function()\n                return text .. (css_file == G_reader_settings:readSetting("copt_css") and "   ★" or "")\n            end,\n            callback = function()\n                self:setStyleSheet(css_file or self.ui.document.default_css)\n            end,\n            hold_callback = function(touchmenu_instance)\n                self:makeDefaultStyleSheet(css_file, text, touchmenu_instance)\n            end,\n            checked_func = function()\n                if not css_file then -- "Auto"\n                    return self.css == self.ui.document.default_css\n                end\n                return css_file == self.css\n            end,\n            separator = separator,\n        }\n    end\n\n    local style_table = {}\n    local obsoleted_table = {}\n\n    table.insert(style_table, getStyleMenuItem(_("None"), ""))\n    table.insert(style_table, getStyleMenuItem(_("Auto"), nil, true))\n\n    local css_files = {}\n    for f in lfs.dir("./data") do\n        if lfs.attributes("./data/"..f, "mode") == "file" and string.match(f, "%.css$") then\n            css_files[f] = "./data/"..f\n        end\n    end\n    -- Add the 3 main styles\n    if css_files["epub.css"] then\n        table.insert(style_table, getStyleMenuItem(_("HTML / EPUB (epub.css)"), css_files["epub.css"]))\n        css_files["epub.css"] = nil\n    end\n    if css_files["html5.css"] then\n        table.insert(style_table, getStyleMenuItem(_("HTML5 (html5.css)"), css_files["html5.css"]))\n        css_files["html5.css"] = nil\n    end\n    if css_files["fb2.css"] then\n        table.insert(style_table, getStyleMenuItem(_("FictionBook (fb2.css)"), css_files["fb2.css"], true))\n        css_files["fb2.css"] = nil\n    end\n    -- Add the obsoleted ones to the Obsolete sub menu\n    local obsoleted_css = {} -- for check_func of the Obsolete sub menu itself\n    for __, css in ipairs(OBSOLETED_CSS) do\n        obsoleted_css[css_files[css]] = css\n        if css_files[css] then\n            table.insert(obsoleted_table, getStyleMenuItem(css, css_files[css]))\n            css_files[css] = nil\n        end\n    end\n    -- Sort and add the remaining (user added) files if any\n    local user_files = {}\n    for css, css_file in pairs(css_files) do\n        table.insert(user_files, css)\n    end\n    table.sort(user_files)\n    for __, css in ipairs(user_files) do\n        table.insert(style_table, getStyleMenuItem(css, css_files[css]))\n    end\n\n    style_table[#style_table].separator = true\n    table.insert(style_table, {\n        text_func = function()\n            local text = _("Obsolete")\n            if obsoleted_css[self.css] then\n                text = T(_("Obsolete (%1)"), BD.filename(obsoleted_css[self.css]))\n            end\n            if obsoleted_css[G_reader_settings:readSetting("copt_css")] then\n                text = text .. "   ★"\n            end\n            return text\n        end,\n        sub_item_table = obsoleted_table,\n        checked_func = function()\n            return obsoleted_css[self.css] ~= nil\n        end\n    })\n    return style_table\nend\n\nfunction ReaderTypeset:onApplyStyleSheet()\n    local tweaks_css = self.ui.styletweak:getCssText()\n    self.ui.document:setStyleSheet(self.css, tweaks_css)\n    self.ui:handleEvent(Event:new("UpdatePos"))\n    return true\nend\n\nfunction ReaderTypeset:setStyleSheet(new_css)\n    if new_css ~= self.css then\n        self.css = new_css\n        local tweaks_css = self.ui.styletweak:getCssText()\n        self.ui.document:setStyleSheet(new_css, tweaks_css)\n        self.ui:handleEvent(Event:new("UpdatePos"))\n    end\nend\n\nfunction ReaderTypeset:setEmbededStyleSheetOnly()\n    if self.css ~= nil then\n        -- clear applied css\n        self.ui.document:setStyleSheet("")\n        self.ui.document:setEmbeddedStyleSheet(1)\n        self.css = nil\n        self.ui:handleEvent(Event:new("UpdatePos"))\n    end\nend\n\nfunction ReaderTypeset:toggleEmbeddedStyleSheet(toggle)\n    if not toggle then\n        self.embedded_css = false\n        self:setStyleSheet(self.ui.document.default_css)\n        self.ui.document:setEmbeddedStyleSheet(0)\n    else\n        self.embedded_css = true\n        --self:setStyleSheet(self.ui.document.default_css)\n        self.ui.document:setEmbeddedStyleSheet(1)\n    end\n    self.ui:handleEvent(Event:new("UpdatePos"))\nend\n\nfunction ReaderTypeset:toggleEmbeddedFonts(toggle)\n    if not toggle then\n        self.embedded_fonts = false\n        self.ui.document:setEmbeddedFonts(0)\n    else\n        self.embedded_fonts = true\n        self.ui.document:setEmbeddedFonts(1)\n    end\n    self.ui:handleEvent(Event:new("UpdatePos"))\nend\n\n-- crengine enhanced block rendering feature/flags (see crengine/include/lvrend.h):\n--                                               legacy flat book web\n-- ENHANCED                           0x00000001          x    x   x\n-- ALLOW_PAGE_BREAK_WHEN_NO_CONTENT   0x00000002                   x\n--\n-- COLLAPSE_VERTICAL_MARGINS          0x00000010          x    x   x\n-- ALLOW_VERTICAL_NEGATIVE_MARGINS    0x00000020          x    x   x\n-- ALLOW_NEGATIVE_COLLAPSED_MARGINS   0x00000040                   x\n--\n-- ENSURE_MARGIN_AUTO_ALIGNMENT       0x00000100               x   x\n-- ALLOW_HORIZONTAL_NEGATIVE_MARGINS  0x00000200                   x\n-- ALLOW_HORIZONTAL_BLOCK_OVERFLOW    0x00000400                   x\n-- ALLOW_HORIZONTAL_PAGE_OVERFLOW     0x00000800                   x\n--\n-- USE_W3C_BOX_MODEL                  0x00001000          x    x   x\n-- ALLOW_STYLE_W_H_ABSOLUTE_UNITS     0x00002000                   x\n-- ENSURE_STYLE_WIDTH                 0x00004000               x   x\n-- ENSURE_STYLE_HEIGHT                0x00008000                   x\n--\n-- WRAP_FLOATS                        0x00010000          x    x   x\n-- PREPARE_FLOATBOXES                 0x00020000          x    x   x\n-- FLOAT_FLOATBOXES                   0x00040000               x   x\n-- DO_NOT_CLEAR_OWN_FLOATS            0x00100000               x   x\n-- ALLOW_EXACT_FLOATS_FOOTPRINTS      0x00200000               x   x\n--\n-- BOX_INLINE_BLOCKS                  0x01000000          x    x   x\n-- COMPLETE_INCOMPLETE_TABLES         0x02000000          x    x   x\n\nlocal BLOCK_RENDERING_FLAGS = {\n    0x00000000, -- legacy block rendering\n    0x03030031, -- flat mode (with prepared floatBoxes, so inlined, to avoid display hash mismatch)\n    0x03375131, -- book mode (floating floatBoxes, limited widths support)\n    0x7FFFFFFF, -- web mode, all features/flags\n}\n\nfunction ReaderTypeset:setBlockRenderingMode(mode)\n    -- mode starts for now with 0 = legacy, so we may later be able\n    -- to remove it and then start with 1 = flat\n    -- (Ensure we don\'t crash if we added and removed some options)\n    if mode + 1 > #BLOCK_RENDERING_FLAGS then\n        mode = #BLOCK_RENDERING_FLAGS - 1\n    end\n    local flags = BLOCK_RENDERING_FLAGS[mode + 1]\n    if not flags then\n        return\n    end\n    self.block_rendering_mode = mode\n    if self.ensure_saner_block_rendering_flags then -- see next function\n        -- Don\'t enable BOX_INLINE_BLOCKS\n        -- inlineBoxes have been around and allowed on older DOM_VERSION\n        -- for a few weeks - let\'s disable it: it may break highlights\n        -- made during this time, but may resurrect others made during\n        -- a longer previous period of time.\n        flags = bit.band(flags, bit.bnot(0x01000000))\n        -- Don\'t enable COMPLETE_INCOMPLETE_TABLES, as they may add\n        -- many boxing elements around huge amount of text, and break\n        -- some past highlights made on the non-boxed elements.\n        flags = bit.band(flags, bit.bnot(0x02000000))\n    end\n    self.ui.document:setBlockRenderingFlags(flags)\n    self.ui:handleEvent(Event:new("UpdatePos"))\nend\n\nfunction ReaderTypeset:ensureSanerBlockRenderingFlags(mode)\n    -- Called by ReaderRolling:onReadSettings() when old\n    -- DOM version requested, before normalized xpointers,\n    -- asking us to unset some of the flags set previously.\n    self.ensure_saner_block_rendering_flags = true\n    self:setBlockRenderingMode(self.block_rendering_mode)\nend\n\nfunction ReaderTypeset:toggleImageScaling(toggle)\n    if toggle and (toggle == true or toggle == 1) then\n        self.smooth_scaling = true\n        self.ui.document:setImageScaling(true)\n    else\n        self.smooth_scaling = false\n        self.ui.document:setImageScaling(false)\n    end\n    self.ui:handleEvent(Event:new("UpdatePos"))\nend\n\nfunction ReaderTypeset:toggleNightmodeImages(toggle)\n    if toggle and (toggle == true or toggle == 1) then\n        self.nightmode_images = true\n        self.ui.document:setNightmodeImages(true)\n    else\n        self.nightmode_images = false\n        self.ui.document:setNightmodeImages(false)\n    end\n    self.ui:handleEvent(Event:new("UpdatePos"))\nend\n\nfunction ReaderTypeset:toggleTxtPreFormatted(toggle)\n    self.ui.document:setTxtPreFormatted(toggle)\n    self.ui:handleEvent(Event:new("UpdatePos"))\nend\n\nfunction ReaderTypeset:setRenderDPI(dpi)\n    self.render_dpi = dpi\n    self.ui.document:setRenderDPI(dpi)\n    self.ui:handleEvent(Event:new("UpdatePos"))\nend\n\nfunction ReaderTypeset:addToMainMenu(menu_items)\n    -- insert table to main reader menu\n    menu_items.set_render_style = {\n        text = self.css_menu_title,\n        sub_item_table = self:genStyleSheetMenu(),\n    }\nend\n\nfunction ReaderTypeset:makeDefaultStyleSheet(css, text, touchmenu_instance)\n    UIManager:show(ConfirmBox:new{\n        text = T( _("Set default style to %1?"), BD.filename(text)),\n        ok_callback = function()\n            G_reader_settings:saveSetting("copt_css", css)\n            if touchmenu_instance then touchmenu_instance:updateItems() end\n        end,\n    })\nend\n\nfunction ReaderTypeset:onSetPageHorizMargins(h_margins, when_applied_callback)\n    self.unscaled_margins = { h_margins[1], self.unscaled_margins[2], h_margins[2], self.unscaled_margins[4] }\n    self.ui:handleEvent(Event:new("SetPageMargins", self.unscaled_margins, when_applied_callback))\nend\n\nfunction ReaderTypeset:onSetPageTopMargin(t_margin, when_applied_callback)\n    self.unscaled_margins = { self.unscaled_margins[1], t_margin, self.unscaled_margins[3], self.unscaled_margins[4] }\n    if self.sync_t_b_page_margins then\n        self.unscaled_margins[4] = t_margin\n        -- Let ConfigDialog know so it can update it on screen and have it saved on quit\n        self.ui.document.configurable.b_page_margin = t_margin\n    end\n    self.ui:handleEvent(Event:new("SetPageMargins", self.unscaled_margins, when_applied_callback))\nend\n\nfunction ReaderTypeset:onSetPageBottomMargin(b_margin, when_applied_callback)\n    self.unscaled_margins = { self.unscaled_margins[1], self.unscaled_margins[2], self.unscaled_margins[3], b_margin }\n    if self.sync_t_b_page_margins then\n        self.unscaled_margins[2] = b_margin\n        -- Let ConfigDialog know so it can update it on screen and have it saved on quit\n        self.ui.document.configurable.t_page_margin = b_margin\n    end\n    self.ui:handleEvent(Event:new("SetPageMargins", self.unscaled_margins, when_applied_callback))\nend\n\nfunction ReaderTypeset:onSetPageTopAndBottomMargin(t_b_margins, when_applied_callback)\n    local t_margin, b_margin = t_b_margins[1], t_b_margins[2]\n    self.unscaled_margins = { self.unscaled_margins[1], t_margin, self.unscaled_margins[3], b_margin }\n    if t_margin ~= b_margin then\n        -- Set Sync T/B Margins toggle to off, as user explicitly made them differ\n        self.sync_t_b_page_margins = false\n        self.ui.document.configurable.sync_t_b_page_margins = 0\n    end\n    self.ui:handleEvent(Event:new("SetPageMargins", self.unscaled_margins, when_applied_callback))\nend\n\nfunction ReaderTypeset:onSyncPageTopBottomMargins(toggle, when_applied_callback)\n    self.sync_t_b_page_margins = not self.sync_t_b_page_margins\n    if self.sync_t_b_page_margins then\n        -- Adjust current top and bottom margins if needed\n        if self.unscaled_margins[2] ~= self.unscaled_margins[4] then\n            -- Taking the rounded mean can change the vertical page height,\n            -- and so the previous lines layout. We could have used the mean\n            -- for the top, and the delta from the mean for the bottom (and\n            -- have them possibly not equal), but as these are unscaled here,\n            -- and later scaled, the end result could still be different.\n            -- So just take the mean and make them equal.\n            local mean_margin = Math.round((self.unscaled_margins[2] + self.unscaled_margins[4]) / 2)\n            self.ui.document.configurable.t_page_margin = mean_margin\n            self.ui.document.configurable.b_page_margin = mean_margin\n            self.unscaled_margins = { self.unscaled_margins[1], mean_margin, self.unscaled_margins[3], mean_margin }\n            self.ui:handleEvent(Event:new("SetPageMargins", self.unscaled_margins, when_applied_callback))\n            when_applied_callback = nil\n        end\n    end\n    if when_applied_callback then\n        when_applied_callback()\n    end\nend\n\nfunction ReaderTypeset:onSetPageMargins(margins, when_applied_callback)\n    local left = Screen:scaleBySize(margins[1])\n    local top = Screen:scaleBySize(margins[2])\n    local right = Screen:scaleBySize(margins[3])\n    local bottom\n    if self.view.footer.reclaim_height then\n        bottom = Screen:scaleBySize(margins[4])\n    else\n        bottom = Screen:scaleBySize(margins[4]) + self.view.footer:getHeight()\n    end\n    self.ui.document:setPageMargins(left, top, right, bottom)\n    self.ui:handleEvent(Event:new("UpdatePos"))\n    if when_applied_callback then\n        -- Provided when hide_on_apply, and ConfigDialog temporarily hidden:\n        -- show an InfoMessage with the unscaled & scaled values,\n        -- and call when_applied_callback on dismiss\n        UIManager:show(InfoMessage:new{\n            text = T(_([[\nMargins set to:\n\n  left: %1 (%2px)\n  right: %3 (%4px)\n  top: %5 (%6px)\n  bottom: %7 (%8px)\n\nTap to dismiss.]]),\n            margins[1], left, margins[3], right, margins[2], top, margins[4], bottom),\n            dismiss_callback = when_applied_callback,\n        })\n    end\nend\n\nreturn ReaderTypeset\n', 'message': 'Implement persistent global style setting\n', 'author_name': 'Frans de Jonge', 'lang': 'lua'}