mirror of
https://github.com/koreader/koreader
synced 2024-11-04 12:00:25 +00:00
BookInfo: add page information (#10255)
This commit is contained in:
parent
d992fd3cfe
commit
a3080b042a
@ -5,18 +5,16 @@ This module provides a way to display book information (filename and book metada
|
||||
local BD = require("ui/bidi")
|
||||
local DocSettings = require("docsettings")
|
||||
local DocumentRegistry = require("document/documentregistry")
|
||||
local ImageViewer = require("ui/widget/imageviewer")
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
local KeyValuePage = require("ui/widget/keyvaluepage")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
local filemanagerutil = require("apps/filemanager/filemanagerutil")
|
||||
local lfs = require("libs/libkoreader-lfs")
|
||||
local util = require("util")
|
||||
local _ = require("gettext")
|
||||
local Screen = require("device").screen
|
||||
|
||||
local BookInfo = WidgetContainer:extend{
|
||||
bookinfo_menu_title = _("Book information"),
|
||||
}
|
||||
|
||||
function BookInfo:init()
|
||||
@ -27,7 +25,7 @@ end
|
||||
|
||||
function BookInfo:addToMainMenu(menu_items)
|
||||
menu_items.book_info = {
|
||||
text = self.bookinfo_menu_title,
|
||||
text = _("Book information"),
|
||||
callback = function()
|
||||
self:onShowBookInfo()
|
||||
end,
|
||||
@ -37,117 +35,90 @@ end
|
||||
function BookInfo:show(file, book_props)
|
||||
local kv_pairs = {}
|
||||
|
||||
local directory, filename = util.splitFilePathName(file)
|
||||
local filename_without_suffix, filetype = util.splitFileNameSuffix(filename) -- luacheck: no unused
|
||||
if filetype:lower() == "zip" then
|
||||
local filename_without_sub_suffix, sub_filetype = util.splitFileNameSuffix(filename_without_suffix) -- luacheck: no unused
|
||||
sub_filetype = sub_filetype:lower()
|
||||
local supported_sub_filetypes = { "fb2", "htm", "html", "log", "md", "txt" }
|
||||
|
||||
for __, t in ipairs(supported_sub_filetypes) do
|
||||
if sub_filetype == t then
|
||||
filetype = sub_filetype .. "." .. filetype
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
local file_size = lfs.attributes(file, "size") or 0
|
||||
local file_modification = lfs.attributes(file, "modification") or 0
|
||||
-- File section
|
||||
local folder, filename = util.splitFilePathName(file)
|
||||
local __, filetype = filemanagerutil.splitFileNameType(filename)
|
||||
local attr = lfs.attributes(file)
|
||||
local file_size = attr.size or 0
|
||||
local size_f = util.getFriendlySize(file_size)
|
||||
local size_b = util.getFormattedSize(file_size)
|
||||
local size = string.format("%s (%s bytes)", size_f, size_b)
|
||||
table.insert(kv_pairs, { _("Filename:"), BD.filename(filename) })
|
||||
table.insert(kv_pairs, { _("Format:"), filetype:upper() })
|
||||
table.insert(kv_pairs, { _("Size:"), size })
|
||||
table.insert(kv_pairs, { _("File date:"), os.date("%Y-%m-%d %H:%M:%S", file_modification) })
|
||||
table.insert(kv_pairs, { _("Folder:"), BD.dirpath(filemanagerutil.abbreviate(directory)), separator = true })
|
||||
table.insert(kv_pairs, { _("Size:"), string.format("%s (%s bytes)", size_f, size_b) })
|
||||
table.insert(kv_pairs, { _("File date:"), os.date("%Y-%m-%d %H:%M:%S", attr.modification) })
|
||||
table.insert(kv_pairs, { _("Folder:"), BD.dirpath(filemanagerutil.abbreviate(folder)), separator = true })
|
||||
|
||||
-- Book section
|
||||
-- book_props may be provided if caller already has them available
|
||||
-- but it may lack "pages", that we may get from sidecar file
|
||||
if not book_props or not book_props.pages then
|
||||
book_props = self:getBookProps(file, book_props)
|
||||
end
|
||||
|
||||
local title = book_props.title
|
||||
if title == "" or title == nil then title = _("N/A") end
|
||||
table.insert(kv_pairs, { _("Title:"), BD.auto(title) })
|
||||
|
||||
local authors = book_props.authors
|
||||
if authors == "" or authors == nil then
|
||||
authors = _("N/A")
|
||||
elseif authors:find("\n") then -- BD auto isolate each author
|
||||
authors = util.splitToArray(authors, "\n")
|
||||
for i=1, #authors do
|
||||
authors[i] = BD.auto(authors[i])
|
||||
local values_lang
|
||||
local props = {
|
||||
{ _("Title:"), "title" },
|
||||
{ _("Authors:"), "authors" },
|
||||
{ _("Series:"), "series" },
|
||||
{ _("Pages:"), "pages" },
|
||||
{ _("Language:"), "language" },
|
||||
{ _("Keywords:"), "keywords" },
|
||||
{ _("Description:"), "description" },
|
||||
}
|
||||
for _i, v in ipairs(props) do
|
||||
local prop_text, prop_key = unpack(v)
|
||||
local prop = book_props[prop_key]
|
||||
if prop == nil or prop == "" then
|
||||
prop = _("N/A")
|
||||
elseif prop_key == "title" then
|
||||
prop = BD.auto(prop)
|
||||
elseif prop_key == "authors" or prop_key == "keywords" then
|
||||
if prop:find("\n") then -- BD auto isolate each entry
|
||||
prop = util.splitToArray(prop, "\n")
|
||||
for i = 1, #prop do
|
||||
prop[i] = BD.auto(prop[i])
|
||||
end
|
||||
prop = table.concat(prop, "\n")
|
||||
else
|
||||
prop = BD.auto(prop)
|
||||
end
|
||||
elseif prop_key == "series" then
|
||||
-- If we were fed a BookInfo book_props (e.g., covermenu), series index is in a separate field
|
||||
if book_props.series_index then
|
||||
-- Here, we're assured that series_index is a Lua number, so round integers are automatically
|
||||
-- displayed without decimals
|
||||
prop = prop .. " #" .. book_props.series_index
|
||||
else
|
||||
-- But here, if we have a plain doc_props series with an index, drop empty decimals from round integers.
|
||||
prop = prop:gsub("(#%d+)%.0+$", "%1")
|
||||
end
|
||||
elseif prop_key == "language" then
|
||||
-- Get a chance to have title, authors... rendered with alternate
|
||||
-- glyphs for the book language (e.g. japanese book in chinese UI)
|
||||
values_lang = prop
|
||||
elseif prop_key == "description" then
|
||||
-- Description may (often in EPUB, but not always) or may not (rarely in PDF) be HTML
|
||||
prop = util.htmlToPlainTextIfHtml(prop)
|
||||
end
|
||||
authors = table.concat(authors, "\n")
|
||||
else
|
||||
authors = BD.auto(authors)
|
||||
table.insert(kv_pairs, { prop_text, prop })
|
||||
end
|
||||
table.insert(kv_pairs, { _("Authors:"), authors })
|
||||
|
||||
local series = book_props.series
|
||||
if series == "" or series == nil then
|
||||
series = _("N/A")
|
||||
else
|
||||
-- If we were fed a BookInfo book_props (e.g., covermenu), series index is in a separate field
|
||||
if book_props.series_index then
|
||||
-- Here, we're assured that series_index is a Lua number, so round integers are automatically displayed without decimals
|
||||
series = book_props.series .. " #" .. book_props.series_index
|
||||
else
|
||||
-- But here, if we have a plain doc_props series with an index, drop empty decimals from round integers.
|
||||
series = book_props.series:gsub("(#%d+)%.0+$", "%1")
|
||||
end
|
||||
end
|
||||
table.insert(kv_pairs, { _("Series:"), BD.auto(series) })
|
||||
|
||||
local pages = book_props.pages
|
||||
if pages == "" or pages == nil then pages = _("N/A") end
|
||||
table.insert(kv_pairs, { _("Pages:"), pages })
|
||||
|
||||
local language = book_props.language
|
||||
if language == "" or language == nil then language = _("N/A") end
|
||||
table.insert(kv_pairs, { _("Language:"), language })
|
||||
|
||||
local keywords = book_props.keywords
|
||||
if keywords == "" or keywords == nil then
|
||||
keywords = _("N/A")
|
||||
elseif keywords:find("\n") then -- BD auto isolate each keywords
|
||||
keywords = util.splitToArray(keywords, "\n")
|
||||
for i=1, #keywords do
|
||||
keywords[i] = BD.auto(keywords[i])
|
||||
end
|
||||
keywords = table.concat(keywords, "\n")
|
||||
else
|
||||
keywords = BD.auto(keywords)
|
||||
end
|
||||
table.insert(kv_pairs, { _("Keywords:"), keywords })
|
||||
|
||||
local description = book_props.description
|
||||
if description == "" or description == nil then
|
||||
description = _("N/A")
|
||||
else
|
||||
-- Description may (often in EPUB, but not always) or may not (rarely
|
||||
-- in PDF) be HTML.
|
||||
description = util.htmlToPlainTextIfHtml(book_props.description)
|
||||
end
|
||||
-- (We don't BD wrap description: it may be multi-lines, and the value we set
|
||||
-- here may be viewed in a TextViewer that has auto_para_direction=true, which
|
||||
-- will show the right thing, that'd we rather not mess with BD wrapping.)
|
||||
table.insert(kv_pairs, { _("Description:"), description })
|
||||
|
||||
local is_doc = self.document and true or false
|
||||
local viewCoverImage = function()
|
||||
self:onShowBookCover(file)
|
||||
end
|
||||
table.insert(kv_pairs, { _("Cover image:"), _("Tap to display"), callback=viewCoverImage })
|
||||
table.insert(kv_pairs, { _("Cover image:"), _("Tap to display"), callback=viewCoverImage, separator=is_doc })
|
||||
|
||||
-- Get a chance to have title, authors... rendered with alternate
|
||||
-- glyphs for the book language (e.g. japanese book in chinese UI)
|
||||
local values_lang = nil
|
||||
if book_props.language and book_props.language ~= "" then
|
||||
values_lang = book_props.language
|
||||
-- Page section
|
||||
if is_doc then
|
||||
local lines_nb, words_nb = self:getCurrentPageLineWordCounts()
|
||||
if lines_nb == 0 then
|
||||
lines_nb = _("N/A")
|
||||
words_nb = _("N/A")
|
||||
end
|
||||
table.insert(kv_pairs, { _("Current page lines:"), lines_nb })
|
||||
table.insert(kv_pairs, { _("Current page words:"), words_nb })
|
||||
end
|
||||
|
||||
local KeyValuePage = require("ui/widget/keyvaluepage")
|
||||
local widget = KeyValuePage:new{
|
||||
title = _("Book information"),
|
||||
value_overflow_align = "right",
|
||||
@ -269,6 +240,7 @@ function BookInfo:onShowBookCover(file)
|
||||
if document then
|
||||
local cover_bb = document:getCoverPageImage()
|
||||
if cover_bb then
|
||||
local ImageViewer = require("ui/widget/imageviewer")
|
||||
local imgviewer = ImageViewer:new{
|
||||
image = cover_bb,
|
||||
with_title_bar = false,
|
||||
@ -284,4 +256,41 @@ function BookInfo:onShowBookCover(file)
|
||||
end
|
||||
end
|
||||
|
||||
function BookInfo:getCurrentPageLineWordCounts()
|
||||
local lines_nb, words_nb = 0, 0
|
||||
if self.ui.rolling then
|
||||
local res = self.ui.document._document:getTextFromPositions(0, 0, Screen:getWidth(), Screen:getHeight(),
|
||||
false, false) -- do not highlight
|
||||
if res then
|
||||
lines_nb = #self.ui.document:getScreenBoxesFromPositions(res.pos0, res.pos1, true)
|
||||
for word in util.gsplit(res.text, "[%s%p]+", false) do
|
||||
if util.hasCJKChar(word) then
|
||||
for char in util.gsplit(word, "[\192-\255][\128-\191]+", true) do
|
||||
words_nb = words_nb + 1
|
||||
end
|
||||
else
|
||||
words_nb = words_nb + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local page_boxes = self.ui.document:getTextBoxes(self.ui:getCurrentPage())
|
||||
if page_boxes and page_boxes[1][1].word then
|
||||
lines_nb = #page_boxes
|
||||
for _, line in ipairs(page_boxes) do
|
||||
if #line == 1 and line[1].word == "" then -- empty line
|
||||
lines_nb = lines_nb - 1
|
||||
else
|
||||
words_nb = words_nb + #line
|
||||
local last_word = line[#line].word
|
||||
if last_word:sub(-1) == "-" and last_word ~= "-" then -- hyphenated
|
||||
words_nb = words_nb - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return lines_nb, words_nb
|
||||
end
|
||||
|
||||
return BookInfo
|
||||
|
@ -3,10 +3,8 @@ This module contains miscellaneous helper functions for FileManager
|
||||
]]
|
||||
|
||||
local BD = require("ui/bidi")
|
||||
local ConfirmBox = require("ui/widget/confirmbox")
|
||||
local Device = require("device")
|
||||
local DocSettings = require("docsettings")
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local ffiutil = require("ffi/util")
|
||||
local util = require("util")
|
||||
@ -35,6 +33,24 @@ function filemanagerutil.abbreviate(path)
|
||||
return path
|
||||
end
|
||||
|
||||
function filemanagerutil.splitFileNameType(filename)
|
||||
local filename_without_suffix, filetype = util.splitFileNameSuffix(filename)
|
||||
filetype = filetype:lower()
|
||||
if filetype == "zip" then
|
||||
local filename_without_sub_suffix, sub_filetype = util.splitFileNameSuffix(filename_without_suffix)
|
||||
sub_filetype = sub_filetype:lower()
|
||||
local supported_sub_filetypes = { "fb2", "htm", "html", "log", "md", "rtf", "txt", }
|
||||
for _, t in ipairs(supported_sub_filetypes) do
|
||||
if sub_filetype == t then
|
||||
filetype = sub_filetype .. "." .. filetype
|
||||
filename_without_suffix = filename_without_sub_suffix
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return filename_without_suffix, filetype
|
||||
end
|
||||
|
||||
-- Purge doc settings in sidecar directory
|
||||
function filemanagerutil.purgeSettings(file)
|
||||
local file_abs_path = ffiutil.realpath(file)
|
||||
@ -125,7 +141,8 @@ function filemanagerutil.genResetSettingsButton(file, caller_callback, button_di
|
||||
id = "reset", -- used by covermenu
|
||||
enabled = not button_disabled and DocSettings:hasSidecarFile(ffiutil.realpath(file)),
|
||||
callback = function()
|
||||
UIManager:show(ConfirmBox:new{
|
||||
local ConfirmBox = require("ui/widget/confirmbox")
|
||||
local confirmbox = ConfirmBox:new{
|
||||
text = T(_("Reset this document?") .. "\n\n%1\n\n" ..
|
||||
_("Document progress, settings, bookmarks, highlights and notes will be permanently lost."),
|
||||
BD.filepath(file)),
|
||||
@ -135,7 +152,8 @@ function filemanagerutil.genResetSettingsButton(file, caller_callback, button_di
|
||||
require("readhistory"):fileSettingsPurged(file)
|
||||
caller_callback()
|
||||
end,
|
||||
})
|
||||
}
|
||||
UIManager:show(confirmbox)
|
||||
end,
|
||||
}
|
||||
end
|
||||
@ -178,6 +196,7 @@ end
|
||||
|
||||
-- Generate "Execute script" file dialog button
|
||||
function filemanagerutil.genExecuteScriptButton(file, caller_callback)
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
return {
|
||||
-- @translators This is the script's programming language (e.g., shell or python)
|
||||
text = T(_("Execute %1 script"), util.getScriptType(file)),
|
||||
|
Loading…
Reference in New Issue
Block a user