2020-11-18 20:30:22 +00:00
|
|
|
-- plugin for saving a cover image to a file and scale it to fit the screen
|
|
|
|
|
2020-11-10 14:00:56 +00:00
|
|
|
local Device = require("device")
|
|
|
|
|
2020-11-28 12:58:44 +00:00
|
|
|
if not (Device.isAndroid() or Device.isEmulator() or Device.isRemarkable() or Device.isPocketBook()) then
|
2020-11-10 14:00:56 +00:00
|
|
|
return { disabled = true }
|
|
|
|
end
|
|
|
|
|
2020-11-18 20:30:22 +00:00
|
|
|
local Blitbuffer = require("ffi/blitbuffer")
|
2020-11-10 14:00:56 +00:00
|
|
|
local InfoMessage = require("ui/widget/infomessage")
|
|
|
|
local UIManager = require("ui/uimanager")
|
|
|
|
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
2020-11-18 20:30:22 +00:00
|
|
|
local RenderImage = require("ui/renderimage")
|
2020-11-10 14:00:56 +00:00
|
|
|
local ffiutil = require("ffi/util")
|
|
|
|
local lfs = require("libs/libkoreader-lfs")
|
|
|
|
local logger = require("logger")
|
|
|
|
local util = require("util")
|
|
|
|
local _ = require("gettext")
|
|
|
|
local T = require("ffi/util").template
|
|
|
|
|
|
|
|
local function pathOk(filename)
|
|
|
|
local path, name = util.splitFilePathName(filename)
|
|
|
|
if not Device:isValidPath(path) then -- isValidPath expects a trailing slash
|
|
|
|
return false, T(_("Path \"%1\" isn't in a writable location."), path)
|
|
|
|
elseif not util.pathExists(path:gsub("/$", "")) then -- pathExists expects no trailing slash
|
|
|
|
return false, T(_("The path \"%1\" doesn't exist."), path)
|
|
|
|
elseif name == "" then
|
|
|
|
return false, _("Please enter a filename at the end of the path.")
|
|
|
|
elseif lfs.attributes(filename, "mode") == "directory" then
|
|
|
|
return false, T(_("The path \"%1\" must point to a file, but it points to a directory."), filename)
|
|
|
|
end
|
|
|
|
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
2020-11-28 12:57:33 +00:00
|
|
|
local function getExtension(filename)
|
|
|
|
local _, name = util.splitFilePathName(filename)
|
|
|
|
return util.getFileNameSuffix(name):lower()
|
|
|
|
end
|
|
|
|
|
2020-11-10 14:00:56 +00:00
|
|
|
local CoverImage = WidgetContainer:new{
|
|
|
|
name = "coverimage",
|
|
|
|
is_doc_only = true,
|
|
|
|
}
|
|
|
|
|
|
|
|
function CoverImage:init()
|
|
|
|
self.cover_image_path = G_reader_settings:readSetting("cover_image_path") or "cover.png"
|
2020-11-28 12:57:33 +00:00
|
|
|
self.cover_image_format = G_reader_settings:readSetting("cover_image_format") or "auto"
|
|
|
|
self.cover_image_extension = getExtension(self.cover_image_path)
|
2020-11-18 20:30:22 +00:00
|
|
|
self.cover_image_background = G_reader_settings:readSetting("cover_image_background") or "black"
|
2020-11-10 14:00:56 +00:00
|
|
|
self.cover_image_fallback_path = G_reader_settings:readSetting("cover_image_fallback_path") or "cover_fallback.png"
|
|
|
|
self.enabled = G_reader_settings:isTrue("cover_image_enabled")
|
|
|
|
self.fallback = G_reader_settings:isTrue("cover_image_fallback")
|
|
|
|
self.ui.menu:registerToMainMenu(self)
|
2020-11-18 20:30:22 +00:00
|
|
|
|
2020-11-10 14:00:56 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function CoverImage:_enabled()
|
|
|
|
return self.enabled
|
|
|
|
end
|
|
|
|
|
|
|
|
function CoverImage:_fallback()
|
|
|
|
return self.fallback
|
|
|
|
end
|
|
|
|
|
|
|
|
function CoverImage:cleanUpImage()
|
|
|
|
if self.cover_image_fallback_path == "" or not self.fallback then
|
|
|
|
os.remove(self.cover_image_path)
|
|
|
|
elseif lfs.attributes(self.cover_image_fallback_path, "mode") ~= "file" then
|
|
|
|
UIManager:show(InfoMessage:new{
|
2020-11-12 20:54:42 +00:00
|
|
|
text = T(_("\"%1\" \nis not a valid image file!\nA valid fallback image is required in Cover-Image."), self.cover_image_fallback_path),
|
2020-11-10 14:00:56 +00:00
|
|
|
show_icon = true,
|
|
|
|
timeout = 10,
|
|
|
|
})
|
|
|
|
os.remove(self.cover_image_path)
|
|
|
|
elseif pathOk(self.cover_image_path) then
|
|
|
|
ffiutil.copyFile(self.cover_image_fallback_path, self.cover_image_path)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function CoverImage:createCoverImage(doc_settings)
|
|
|
|
if self.enabled and not doc_settings:readSetting("exclude_cover_image") == true then
|
2020-11-18 20:30:22 +00:00
|
|
|
local cover_image = self.ui.document:getCoverPageImage()
|
|
|
|
if cover_image then
|
|
|
|
local s_w, s_h = Device.screen:getWidth(), Device.screen:getHeight()
|
|
|
|
local i_w, i_h = cover_image:getWidth(), cover_image:getHeight()
|
|
|
|
local scale_factor = math.min(s_w / i_w, s_h / i_h)
|
|
|
|
|
|
|
|
if self.cover_image_background == "none" or scale_factor == 1 then
|
2020-11-28 12:57:33 +00:00
|
|
|
local act_format = self.cover_image_format == "auto" and self.cover_image_extension or self.cover_image_format
|
|
|
|
cover_image:writeToFile(self.cover_image_path, act_format)
|
2020-11-18 20:30:22 +00:00
|
|
|
cover_image:free()
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local scaled_w, scaled_h = math.floor(i_w * scale_factor), math.floor(i_h * scale_factor)
|
|
|
|
cover_image = RenderImage:scaleBlitBuffer(cover_image, scaled_w, scaled_h)
|
|
|
|
|
|
|
|
-- new buffer with screen dimensions,
|
2020-11-28 12:57:33 +00:00
|
|
|
local image = Blitbuffer.new(s_w, s_h, cover_image:getType()) -- new buffer, filled with black
|
2020-11-18 20:30:22 +00:00
|
|
|
if self.cover_image_background == "white" then
|
|
|
|
image:fill(Blitbuffer.COLOR_WHITE)
|
|
|
|
elseif self.cover_image_background == "gray" then
|
|
|
|
image:fill(Blitbuffer.COLOR_GRAY)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- copy scaled image to buffer
|
|
|
|
if s_w > scaled_w then -- move right
|
2020-11-28 12:57:33 +00:00
|
|
|
image:blitFrom(cover_image, math.floor((s_w - scaled_w) / 2), 0, 0, 0, scaled_w, scaled_h)
|
2020-11-18 20:30:22 +00:00
|
|
|
else -- move down
|
2020-11-28 12:57:33 +00:00
|
|
|
image:blitFrom(cover_image, 0, math.floor((s_h - scaled_h) / 2), 0, 0, scaled_w, scaled_h)
|
2020-11-18 20:30:22 +00:00
|
|
|
end
|
|
|
|
cover_image:free()
|
2020-11-28 12:57:33 +00:00
|
|
|
|
|
|
|
local act_format = self.cover_image_format == "auto" and self.cover_image_extension or self.cover_image_format
|
|
|
|
image:writeToFile(self.cover_image_path, act_format)
|
|
|
|
|
2020-11-18 20:30:22 +00:00
|
|
|
image:free()
|
2020-11-10 14:00:56 +00:00
|
|
|
logger.dbg("CoverImage: image written to " .. self.cover_image_path)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function CoverImage:onCloseDocument()
|
|
|
|
logger.dbg("CoverImage: onCloseDocument")
|
|
|
|
if self.fallback then
|
|
|
|
self:cleanUpImage()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function CoverImage:onReaderReady(doc_settings)
|
|
|
|
logger.dbg("CoverImage: onReaderReady")
|
|
|
|
self:createCoverImage(doc_settings)
|
|
|
|
end
|
|
|
|
|
|
|
|
local about_text = _([[
|
|
|
|
This plugin saves the current book cover to a file. That file can be used as a screensaver on certain Android devices, such as Tolinos.
|
|
|
|
|
|
|
|
If enabled, the cover image of the actual file is stored in the selected screensaver file. Books can be excluded if desired.
|
|
|
|
|
|
|
|
If fallback is activated, the fallback file will be copied to the screensaver file on book closing.
|
|
|
|
If the filename is empty or the file doesn't exist, the cover file will be deleted and the system screensaver will be used instead.
|
|
|
|
|
|
|
|
If the fallback image isn't activated, the screensaver image will stay in place after closing a book.]])
|
|
|
|
|
|
|
|
function CoverImage:addToMainMenu(menu_items)
|
|
|
|
menu_items.coverimage = {
|
|
|
|
-- sorting_hint = "document",
|
|
|
|
sorting_hint = "screen",
|
|
|
|
text = _("Save cover image"),
|
|
|
|
checked_func = function()
|
|
|
|
return self.enabled or self.fallback
|
|
|
|
end,
|
|
|
|
sub_item_table = {
|
|
|
|
-- menu entry: about cover image
|
|
|
|
{
|
|
|
|
text = _("About cover image"),
|
|
|
|
keep_menu_open = true,
|
|
|
|
callback = function()
|
|
|
|
UIManager:show(InfoMessage:new{
|
|
|
|
text = about_text,
|
|
|
|
})
|
|
|
|
end,
|
|
|
|
separator = true,
|
|
|
|
},
|
|
|
|
-- menu entry: filename dialog
|
|
|
|
{
|
|
|
|
text = _("Set system screensaver image"),
|
|
|
|
checked_func = function()
|
|
|
|
return self.cover_image_path ~= "" and pathOk(self.cover_image_path)
|
|
|
|
end,
|
|
|
|
help_text = _("The cover of the current book will be stored in this file."),
|
|
|
|
keep_menu_open = true,
|
|
|
|
callback = function(menu)
|
|
|
|
local InputDialog = require("ui/widget/inputdialog")
|
|
|
|
local sample_input
|
|
|
|
sample_input = InputDialog:new{
|
|
|
|
title = _("Screensaver image filename"),
|
|
|
|
input = self.cover_image_path,
|
|
|
|
input_type = "string",
|
|
|
|
description = _("You can enter the filename of the cover image here."),
|
|
|
|
buttons = {
|
|
|
|
{
|
|
|
|
{
|
|
|
|
text = _("Cancel"),
|
|
|
|
callback = function()
|
|
|
|
UIManager:close(sample_input)
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
text = _("Save"),
|
|
|
|
is_enter_default = true,
|
|
|
|
callback = function()
|
|
|
|
local new_cover_image_path = sample_input:getInputText()
|
|
|
|
if new_cover_image_path ~= self.cover_image_path then
|
|
|
|
self:cleanUpImage() -- with old filename
|
|
|
|
self.cover_image_path = new_cover_image_path -- update filename
|
|
|
|
G_reader_settings:saveSetting("cover_image_path", self.cover_image_path)
|
|
|
|
local is_path_ok, is_path_ok_message = pathOk(self.cover_image_path)
|
|
|
|
if self.cover_image_path ~= "" and is_path_ok then
|
|
|
|
self:createCoverImage(self.ui.doc_settings) -- with new filename
|
|
|
|
else
|
|
|
|
self.enabled = false
|
|
|
|
UIManager:show(InfoMessage:new{
|
|
|
|
text = is_path_ok_message,
|
|
|
|
show_icon = true,
|
|
|
|
})
|
|
|
|
end
|
|
|
|
end
|
2020-11-28 12:57:33 +00:00
|
|
|
self.cover_image_extension = getExtension(self.cover_image_path)
|
2020-11-10 14:00:56 +00:00
|
|
|
UIManager:close(sample_input)
|
|
|
|
menu:updateItems()
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
UIManager:show(sample_input)
|
|
|
|
sample_input:onShowKeyboard()
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
-- menu entry: enable
|
|
|
|
{
|
|
|
|
text = _("Save book cover"),
|
|
|
|
checked_func = function()
|
|
|
|
return self:_enabled() and pathOk(self.cover_image_path)
|
|
|
|
end,
|
|
|
|
enabled_func = function()
|
|
|
|
return self.cover_image_path ~= "" and pathOk(self.cover_image_path)
|
|
|
|
end,
|
|
|
|
callback = function()
|
|
|
|
if self.cover_image_path ~= "" then
|
|
|
|
self.enabled = not self.enabled
|
|
|
|
G_reader_settings:saveSetting("cover_image_enabled", self.enabled)
|
|
|
|
if self.enabled then
|
|
|
|
self:createCoverImage(self.ui.doc_settings)
|
|
|
|
else
|
|
|
|
self:cleanUpImage()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
},
|
2020-11-18 20:30:22 +00:00
|
|
|
-- menu entry: scale book cover
|
|
|
|
{
|
2020-11-28 12:57:33 +00:00
|
|
|
text = _("Size, background and format"),
|
2020-11-18 20:30:22 +00:00
|
|
|
enabled_func = function()
|
|
|
|
return self.enabled
|
|
|
|
end,
|
|
|
|
sub_item_table = {
|
|
|
|
{
|
|
|
|
text = _("Scale, black background"),
|
|
|
|
checked_func = function()
|
|
|
|
return self.cover_image_background == "black"
|
|
|
|
end,
|
|
|
|
callback = function()
|
2020-11-28 12:57:33 +00:00
|
|
|
local old_background = self.cover_image_background
|
2020-11-18 20:30:22 +00:00
|
|
|
self.cover_image_background = "black"
|
|
|
|
G_reader_settings:saveSetting("cover_image_background", self.cover_image_background)
|
2020-11-28 12:57:33 +00:00
|
|
|
if self.enabled and old_background ~= self.cover_image_background then
|
2020-11-18 20:30:22 +00:00
|
|
|
self:createCoverImage(self.ui.doc_settings)
|
|
|
|
end
|
2020-11-28 12:57:33 +00:00
|
|
|
end,
|
2020-11-18 20:30:22 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
text = _("Scale, white background"),
|
|
|
|
checked_func = function()
|
|
|
|
return self.cover_image_background == "white"
|
|
|
|
end,
|
|
|
|
callback = function()
|
2020-11-28 12:57:33 +00:00
|
|
|
local old_background = self.cover_image_background
|
2020-11-18 20:30:22 +00:00
|
|
|
self.cover_image_background = "white"
|
|
|
|
G_reader_settings:saveSetting("cover_image_background", self.cover_image_background)
|
2020-11-28 12:57:33 +00:00
|
|
|
if self.enabled and old_background ~= self.cover_image_background then
|
2020-11-18 20:30:22 +00:00
|
|
|
self:createCoverImage(self.ui.doc_settings)
|
|
|
|
end
|
2020-11-28 12:57:33 +00:00
|
|
|
end,
|
2020-11-18 20:30:22 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
text = _("Scale, gray background"),
|
|
|
|
checked_func = function()
|
|
|
|
return self.cover_image_background == "gray"
|
|
|
|
end,
|
|
|
|
callback = function()
|
2020-11-28 12:57:33 +00:00
|
|
|
local old_background = self.cover_image_background
|
2020-11-18 20:30:22 +00:00
|
|
|
self.cover_image_background = "gray"
|
|
|
|
G_reader_settings:saveSetting("cover_image_background", self.cover_image_background)
|
2020-11-28 12:57:33 +00:00
|
|
|
if self.enabled and old_background ~= self.cover_image_background then
|
2020-11-18 20:30:22 +00:00
|
|
|
self:createCoverImage(self.ui.doc_settings)
|
|
|
|
end
|
2020-11-28 12:57:33 +00:00
|
|
|
end,
|
2020-11-18 20:30:22 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
text = _("Original image"),
|
|
|
|
checked_func = function()
|
|
|
|
return self.cover_image_background == "none"
|
|
|
|
end,
|
|
|
|
callback = function()
|
2020-11-28 12:57:33 +00:00
|
|
|
local old_background = self.cover_image_background
|
2020-11-18 20:30:22 +00:00
|
|
|
self.cover_image_background = "none"
|
|
|
|
G_reader_settings:saveSetting("cover_image_background", self.cover_image_background)
|
2020-11-28 12:57:33 +00:00
|
|
|
if self.enabled and old_background ~= self.cover_image_background then
|
2020-11-18 20:30:22 +00:00
|
|
|
self:createCoverImage(self.ui.doc_settings)
|
|
|
|
end
|
2020-11-28 12:57:33 +00:00
|
|
|
end,
|
|
|
|
separator = true,
|
|
|
|
},
|
|
|
|
-- menu entries: File format
|
|
|
|
{
|
|
|
|
text = _("File format derived from filename"),
|
|
|
|
help_text = _("If the file format is not supported, then PNG will be used."),
|
|
|
|
checked_func = function()
|
|
|
|
return self.cover_image_format == "auto"
|
|
|
|
end,
|
|
|
|
callback = function()
|
|
|
|
local old_cover_image_format = self.cover_image_format
|
|
|
|
self.cover_image_format = "auto"
|
|
|
|
G_reader_settings:saveSetting("cover_image_format", self.cover_image_format)
|
|
|
|
if self.enabled and old_cover_image_format ~= self.cover_image_format then
|
|
|
|
self:createCoverImage(self.ui.doc_settings)
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
text = _("PNG file format"),
|
|
|
|
checked_func = function()
|
|
|
|
return self.cover_image_format == "png"
|
|
|
|
end,
|
|
|
|
callback = function()
|
|
|
|
local old_cover_image_format = self.cover_image_format
|
|
|
|
self.cover_image_format = "png"
|
|
|
|
G_reader_settings:saveSetting("cover_image_format", self.cover_image_format)
|
|
|
|
if self.enabled and old_cover_image_format ~= self.cover_image_format then
|
|
|
|
self:createCoverImage(self.ui.doc_settings)
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
text = _("BMP file format"),
|
|
|
|
checked_func = function()
|
|
|
|
return self.cover_image_format == "bmp"
|
|
|
|
end,
|
|
|
|
callback = function()
|
|
|
|
local old_cover_image_format = self.cover_image_format
|
|
|
|
self.cover_image_format = "bmp"
|
|
|
|
G_reader_settings:saveSetting("cover_image_format", self.cover_image_format)
|
|
|
|
if self.enabled and old_cover_image_format ~= self.cover_image_format then
|
|
|
|
self:createCoverImage(self.ui.doc_settings)
|
|
|
|
end
|
|
|
|
end,
|
2020-11-18 20:30:22 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
2020-11-10 14:00:56 +00:00
|
|
|
-- menu entry: exclude this cover
|
|
|
|
{
|
|
|
|
text = _("Exclude this book cover"),
|
|
|
|
checked_func = function()
|
|
|
|
return self.ui and self.ui.doc_settings and self.ui.doc_settings:readSetting("exclude_cover_image") == true
|
|
|
|
end,
|
|
|
|
callback = function()
|
|
|
|
if self.ui.doc_settings:readSetting("exclude_cover_image") == true then
|
|
|
|
self.ui.doc_settings:saveSetting("exclude_cover_image", false)
|
|
|
|
self:createCoverImage(self.ui.doc_settings)
|
|
|
|
else
|
|
|
|
self.ui.doc_settings:saveSetting("exclude_cover_image", true)
|
|
|
|
self:cleanUpImage()
|
|
|
|
end
|
|
|
|
self.ui:saveSettings()
|
|
|
|
end,
|
|
|
|
separator = true,
|
|
|
|
},
|
|
|
|
-- menu entry: set fallback image
|
|
|
|
{
|
|
|
|
text = _("Set fallback image"),
|
|
|
|
checked_func = function()
|
|
|
|
return lfs.attributes(self.cover_image_fallback_path, "mode") == "file"
|
|
|
|
end,
|
|
|
|
keep_menu_open = true,
|
|
|
|
callback = function(menu)
|
|
|
|
local InputDialog = require("ui/widget/inputdialog")
|
|
|
|
local sample_input
|
|
|
|
sample_input = InputDialog:new{
|
|
|
|
title = _("Fallback image filename"),
|
|
|
|
input = self.cover_image_fallback_path,
|
|
|
|
input_type = "string",
|
|
|
|
description = _("Leave this empty to remove the cover when the document is closed."),
|
|
|
|
buttons = {
|
|
|
|
{
|
|
|
|
{
|
|
|
|
text = _("Cancel"),
|
|
|
|
callback = function()
|
|
|
|
UIManager:close(sample_input)
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
text = _("Save"),
|
|
|
|
is_enter_default = true,
|
|
|
|
callback = function()
|
|
|
|
self.cover_image_fallback_path = sample_input:getInputText()
|
|
|
|
G_reader_settings:saveSetting("cover_image_fallback_path", self.cover_image_fallback_path)
|
2020-11-20 09:33:47 +00:00
|
|
|
if lfs.attributes(self.cover_image_fallback_path, "mode") ~= "file"
|
|
|
|
and self.cover_image_fallback_path ~= "" then
|
2020-11-10 14:00:56 +00:00
|
|
|
UIManager:show(InfoMessage:new{
|
|
|
|
text = T(_("\"%1\" \nis not a valid image file!\nA valid fallback image is required in Cover-Image"),
|
|
|
|
self.cover_image_fallback_path),
|
|
|
|
show_icon = true,
|
|
|
|
timeout = 10,
|
|
|
|
})
|
|
|
|
end
|
|
|
|
UIManager:close(sample_input)
|
|
|
|
menu:updateItems()
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
UIManager:show(sample_input)
|
|
|
|
sample_input:onShowKeyboard()
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
-- menu entry: fallback
|
|
|
|
{
|
|
|
|
text = _("Turn on fallback image"),
|
|
|
|
checked_func = function()
|
|
|
|
return self:_fallback()
|
|
|
|
end,
|
|
|
|
callback = function()
|
|
|
|
self.fallback = not self.fallback
|
|
|
|
G_reader_settings:saveSetting("cover_image_fallback", self.fallback)
|
|
|
|
end,
|
|
|
|
separator = true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
return CoverImage
|