mirror of
https://github.com/koreader/koreader.git
synced 2025-12-13 20:36:53 +01:00
[feat] Open with: choose which engine to use for file (#3653)
Fixes #3345 * Add SVG to MuPDF filetypes
This commit is contained in:
@@ -272,6 +272,15 @@ function FileManager:init()
|
||||
-- a little hack to get visual functionality grouping
|
||||
{},
|
||||
{
|
||||
{
|
||||
text = _("Open with…"),
|
||||
enabled = lfs.attributes(file, "mode") == "file"
|
||||
and #(DocumentRegistry:getProviders(file)) > 1,
|
||||
callback = function()
|
||||
UIManager:close(self.file_dialog)
|
||||
DocumentRegistry:showSetProviderButtons(file, FileManager.instance, self, ReaderUI)
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Convert"),
|
||||
enabled = lfs.attributes(file, "mode") == "file"
|
||||
|
||||
@@ -380,15 +380,44 @@ function ReaderUI:showFileManager()
|
||||
end
|
||||
end
|
||||
|
||||
function ReaderUI:showReader(file)
|
||||
function ReaderUI:showReader(file, provider)
|
||||
logger.dbg("show reader ui")
|
||||
require("readhistory"):addItem(file)
|
||||
|
||||
if lfs.attributes(file, "mode") ~= "file" then
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = T(_("File '%1' does not exist."), file)
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
-- prevent crash due to incompatible bookmarks
|
||||
-- @TODO split bookmarks from metadata and do per-engine in conversion
|
||||
provider = provider or DocumentRegistry:getProvider(file)
|
||||
if provider.provider then
|
||||
local doc_settings = DocSettings:open(file)
|
||||
local bookmarks = doc_settings:readSetting("bookmarks") or {}
|
||||
if #bookmarks >= 1 and
|
||||
((provider.provider == "crengine" and type(bookmarks[1].page) == "number") or
|
||||
(provider.provider == "mupdf" and type(bookmarks[1].page) == "string")) then
|
||||
UIManager:show(ConfirmBox:new{
|
||||
text = T(_("The document '%1' with bookmarks or highlights was previously opened with a different engine. To prevent issues, bookmarks need to be deleted before continuing."),
|
||||
file),
|
||||
ok_text = _("Delete"),
|
||||
ok_callback = function()
|
||||
doc_settings:delSetting("bookmarks")
|
||||
doc_settings:close()
|
||||
self:showReaderCoroutine(file, provider)
|
||||
end,
|
||||
cancel_callback = self.showFileManager,
|
||||
})
|
||||
else
|
||||
self:showReaderCoroutine(file, provider)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ReaderUI:showReaderCoroutine(file, provider)
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = T(_("Opening file '%1'."), file),
|
||||
timeout = 0.0,
|
||||
@@ -398,7 +427,7 @@ function ReaderUI:showReader(file)
|
||||
UIManager:nextTick(function()
|
||||
logger.dbg("creating coroutine for showing reader")
|
||||
local co = coroutine.create(function()
|
||||
self:doShowReader(file)
|
||||
self:doShowReader(file, provider)
|
||||
end)
|
||||
local ok, err = coroutine.resume(co)
|
||||
if err ~= nil or ok == false then
|
||||
@@ -410,13 +439,13 @@ function ReaderUI:showReader(file)
|
||||
end
|
||||
|
||||
local _running_instance = nil
|
||||
function ReaderUI:doShowReader(file)
|
||||
function ReaderUI:doShowReader(file, provider)
|
||||
logger.info("opening file", file)
|
||||
-- keep only one instance running
|
||||
if _running_instance then
|
||||
_running_instance:onClose()
|
||||
end
|
||||
local document = DocumentRegistry:openDocument(file)
|
||||
local document = DocumentRegistry:openDocument(file, provider)
|
||||
if not document then
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("No reader engine for this file or invalid file.")
|
||||
|
||||
@@ -26,6 +26,7 @@ local CreDocument = Document:new{
|
||||
fallback_font = G_reader_settings:readSetting("fallback_font") or "Noto Sans CJK SC",
|
||||
default_css = "./data/cr3.css",
|
||||
options = CreOptions,
|
||||
provider = "crengine",
|
||||
provider_name = "Cool Reader Engine",
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ local DjvuDocument = Document:new{
|
||||
options = KoptOptions,
|
||||
koptinterface = nil,
|
||||
color_bb_type = Blitbuffer.TYPE_BBRGB24,
|
||||
provider = "djvulibre",
|
||||
provider_name = "DjVu Libre",
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
This is a registry for document providers
|
||||
]]--
|
||||
|
||||
local ButtonDialogTitle = require("ui/widget/buttondialogtitle")
|
||||
local ConfirmBox = require("ui/widget/confirmbox")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local gettext = require("gettext")
|
||||
local logger = require("logger")
|
||||
local util = require("util")
|
||||
local T = require("ffi/util").template
|
||||
|
||||
local DocumentRegistry = {
|
||||
registry = {},
|
||||
@@ -20,11 +26,35 @@ end
|
||||
|
||||
--- Returns the preferred registered document handler.
|
||||
-- @string file
|
||||
-- @treturn string provider, or nil
|
||||
-- @treturn table provider, or nil
|
||||
function DocumentRegistry:getProvider(file)
|
||||
local providers = self:getProviders(file)
|
||||
|
||||
if providers then
|
||||
-- provider for document
|
||||
local doc_settings_provider = require("docsettings"):open(file):readSetting("provider")
|
||||
|
||||
if doc_settings_provider then
|
||||
for _, provider in ipairs(providers) do
|
||||
if provider.provider.provider == doc_settings_provider then
|
||||
return provider.provider
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- global provider for filetype
|
||||
local filename_suffix = util.getFileNameSuffix(file)
|
||||
local g_settings_provider = G_reader_settings:readSetting("provider")
|
||||
|
||||
if g_settings_provider and g_settings_provider[filename_suffix] then
|
||||
for _, provider in ipairs(providers) do
|
||||
if provider.provider.provider == g_settings_provider[filename_suffix] then
|
||||
return provider.provider
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- highest weighted provider
|
||||
return providers[1].provider
|
||||
end
|
||||
end
|
||||
@@ -54,14 +84,108 @@ function DocumentRegistry:getProviders(file)
|
||||
end
|
||||
end
|
||||
|
||||
function DocumentRegistry:openDocument(file)
|
||||
--- Sets the preferred registered document handler.
|
||||
-- @string file
|
||||
-- @bool all
|
||||
function DocumentRegistry:setProvider(file, provider, all)
|
||||
local _, filename_suffix = util.splitFileNameSuffix(file)
|
||||
|
||||
-- per-document
|
||||
if not all then
|
||||
local DocSettings = require("docsettings"):open(file)
|
||||
DocSettings:saveSetting("provider", provider.provider)
|
||||
DocSettings:flush()
|
||||
-- global
|
||||
else
|
||||
local filetype_provider = G_reader_settings:readSetting("provider") or {}
|
||||
filetype_provider[filename_suffix] = provider.provider
|
||||
G_reader_settings:saveSetting("provider", filetype_provider)
|
||||
end
|
||||
end
|
||||
|
||||
function DocumentRegistry:showSetProviderButtons(file, filemanager_instance, ui, reader_ui)
|
||||
local _, filename_pure = util.splitFilePathName(file)
|
||||
local filename_suffix = util.getFileNameSuffix(file)
|
||||
|
||||
local buttons = {}
|
||||
local providers = self:getProviders(file)
|
||||
|
||||
for _, provider in ipairs(providers) do
|
||||
-- we have no need for extension, mimetype, weights, etc. here
|
||||
provider = provider.provider
|
||||
table.insert(buttons, {
|
||||
{
|
||||
text = string.format("** %s **", provider.provider_name),
|
||||
},
|
||||
})
|
||||
table.insert(buttons, {
|
||||
{
|
||||
text = gettext("Just once"),
|
||||
callback = function()
|
||||
filemanager_instance:onClose()
|
||||
reader_ui:showReader(file, provider)
|
||||
UIManager:close(self.set_provider_dialog)
|
||||
end,
|
||||
},
|
||||
})
|
||||
table.insert(buttons, {
|
||||
{
|
||||
text = gettext("This document"),
|
||||
callback = function()
|
||||
UIManager:show(ConfirmBox:new{
|
||||
text = T(gettext("Always open '%2' with %1?"),
|
||||
provider.provider_name, filename_pure),
|
||||
ok_text = gettext("Always"),
|
||||
ok_callback = function()
|
||||
self:setProvider(file, provider, false)
|
||||
|
||||
filemanager_instance:onClose()
|
||||
reader_ui:showReader(file, provider)
|
||||
UIManager:close(self.set_provider_dialog)
|
||||
end,
|
||||
})
|
||||
end,
|
||||
},
|
||||
})
|
||||
table.insert(buttons, {
|
||||
{
|
||||
text = gettext("All documents"),
|
||||
callback = function()
|
||||
UIManager:show(ConfirmBox:new{
|
||||
text = T(gettext("Always open %2 files with %1?"),
|
||||
provider.provider_name, filename_suffix),
|
||||
ok_text = gettext("Always"),
|
||||
ok_callback = function()
|
||||
self:setProvider(file, provider, true)
|
||||
|
||||
filemanager_instance:onClose()
|
||||
reader_ui:showReader(file, provider)
|
||||
UIManager:close(self.set_provider_dialog)
|
||||
end,
|
||||
})
|
||||
end,
|
||||
},
|
||||
})
|
||||
-- little trick for visual separation
|
||||
table.insert(buttons, {})
|
||||
end
|
||||
|
||||
self.set_provider_dialog = ButtonDialogTitle:new{
|
||||
title = T(gettext("Open %1 with:"), filename_pure),
|
||||
buttons = buttons,
|
||||
}
|
||||
UIManager:show(self.set_provider_dialog)
|
||||
end
|
||||
|
||||
function DocumentRegistry:openDocument(file, provider)
|
||||
-- force a GC, so that any previous document used memory can be reused
|
||||
-- immediately by this new document without having to wait for the
|
||||
-- next regular gc. The second call may help reclaming more memory.
|
||||
collectgarbage()
|
||||
collectgarbage()
|
||||
if not self.registry[file] then
|
||||
local provider = self:getProvider(file)
|
||||
provider = provider or self:getProvider(file)
|
||||
|
||||
if provider ~= nil then
|
||||
local ok, doc = pcall(provider.new, provider, {file = file})
|
||||
if ok then
|
||||
|
||||
@@ -13,6 +13,7 @@ local PdfDocument = Document:new{
|
||||
dc_null = DrawContext.new(),
|
||||
options = KoptOptions,
|
||||
koptinterface = nil,
|
||||
provider = "mupdf",
|
||||
provider_name = "MuPDF",
|
||||
}
|
||||
|
||||
@@ -250,7 +251,7 @@ function PdfDocument:register(registry)
|
||||
registry:addProvider("xhtml", "application/xhtml+xml", self, 100)
|
||||
registry:addProvider("xml", "application/xml", self, 10)
|
||||
registry:addProvider("xps", "application/oxps", self, 100)
|
||||
registry:addProvider("zip", "application/zip", self, 100)
|
||||
registry:addProvider("zip", "application/zip", self, 20)
|
||||
|
||||
--- Picture types ---
|
||||
registry:addProvider("gif", "image/gif", self, 90)
|
||||
@@ -267,7 +268,8 @@ function PdfDocument:register(registry)
|
||||
registry:addProvider("pgm", "image/x‑portable‑bitmap", self, 90)
|
||||
registry:addProvider("png", "image/png", self, 90)
|
||||
registry:addProvider("pnm", "image/x‑portable‑bitmap", self, 90)
|
||||
registry:addProvider("ppm", "image/gif", self, 90)
|
||||
registry:addProvider("ppm", "image/x‑portable‑bitmap", self, 90)
|
||||
registry:addProvider("svg", "image/svg+xml", self, 90)
|
||||
registry:addProvider("tif", "image/tiff", self, 90)
|
||||
registry:addProvider("tiff", "image/tiff", self, 90)
|
||||
-- Windows Media Photo == JPEG XR
|
||||
|
||||
@@ -7,6 +7,7 @@ local PicDocument = Document:new{
|
||||
_document = false,
|
||||
is_pic = true,
|
||||
dc_null = DrawContext.new(),
|
||||
provider = "picdocument",
|
||||
provider_name = "Picture Document",
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
describe("document registry module", function()
|
||||
local DocumentRegistry
|
||||
local DocSettings, DocumentRegistry
|
||||
|
||||
setup(function()
|
||||
require("commonrequire")
|
||||
DocSettings = require("docsettings")
|
||||
DocumentRegistry = require("document/documentregistry")
|
||||
end)
|
||||
|
||||
it("should get preferred rendering engine", function()
|
||||
assert.is_equal("Cool Reader Engine",
|
||||
DocumentRegistry:getProvider("bla.epub").provider_name)
|
||||
assert.is_equal("MuPDF",
|
||||
DocumentRegistry:getProvider("bla.pdf").provider_name)
|
||||
assert.is_equal("crengine",
|
||||
DocumentRegistry:getProvider("bla.epub").provider)
|
||||
assert.is_equal("mupdf",
|
||||
DocumentRegistry:getProvider("bla.pdf").provider)
|
||||
end)
|
||||
|
||||
it("should return all supported rendering engines", function()
|
||||
@@ -20,4 +21,55 @@ describe("document registry module", function()
|
||||
assert.is_equal("MuPDF",
|
||||
providers[2].provider.provider_name)
|
||||
end)
|
||||
|
||||
it("should set per-document setting for rendering engine", function()
|
||||
local path = "../../foo.epub"
|
||||
local pdf_provider = DocumentRegistry:getProvider("bla.pdf")
|
||||
DocumentRegistry:setProvider(path, pdf_provider, false)
|
||||
|
||||
local provider = DocumentRegistry:getProvider(path)
|
||||
|
||||
assert.is_equal("mupdf", provider.provider)
|
||||
|
||||
local docsettings = DocSettings:open(path)
|
||||
docsettings:purge()
|
||||
docsettings:flush()
|
||||
end)
|
||||
it("should set global setting for rendering engine", function()
|
||||
local path = "../../foo.fb2"
|
||||
local pdf_provider = DocumentRegistry:getProvider("bla.pdf")
|
||||
DocumentRegistry:setProvider(path, pdf_provider, true)
|
||||
|
||||
local provider = DocumentRegistry:getProvider(path)
|
||||
|
||||
assert.is_equal("mupdf", provider.provider)
|
||||
|
||||
G_reader_settings:delSetting("provider")
|
||||
end)
|
||||
|
||||
it("should return per-document setting for rendering engine", function()
|
||||
local path = "../../foofoo.epub"
|
||||
local docsettings = DocSettings:open(path)
|
||||
docsettings:saveSetting("provider", "mupdf")
|
||||
docsettings:flush()
|
||||
|
||||
local provider = DocumentRegistry:getProvider(path)
|
||||
|
||||
assert.is_equal("mupdf", provider.provider)
|
||||
|
||||
docsettings:purge()
|
||||
docsettings:flush()
|
||||
end)
|
||||
it("should return global setting for rendering engine", function()
|
||||
local path = "../../foofoo.fb2"
|
||||
local provider_setting = {}
|
||||
provider_setting.fb2 = "mupdf"
|
||||
G_reader_settings:saveSetting("provider", provider_setting)
|
||||
|
||||
local provider = DocumentRegistry:getProvider(path)
|
||||
|
||||
assert.is_equal("mupdf", provider.provider)
|
||||
|
||||
G_reader_settings:delSetting("provider")
|
||||
end)
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user