126 lines
4.1 KiB
Lua
126 lines
4.1 KiB
Lua
-- lua/plugins/pdf-viewer.lua
|
|
-- PDF viewer using pdftoppm + image.nvim (kitty/magick backend)
|
|
-- Requires: poppler (brew install poppler), kitty terminal
|
|
|
|
return {
|
|
'3rd/image.nvim',
|
|
opts = {
|
|
backend = 'kitty',
|
|
processor = 'magick_cli',
|
|
integrations = {
|
|
markdown = { enabled = true },
|
|
},
|
|
},
|
|
config = function(_, opts)
|
|
local image = require 'image'
|
|
image.setup(opts)
|
|
|
|
local pdf_state = {}
|
|
|
|
-- Open PDF: convert all pages to PNG via pdftoppm, then display page 1
|
|
vim.api.nvim_create_autocmd('BufReadPost', {
|
|
pattern = '*.pdf',
|
|
callback = function()
|
|
local pdf_path = vim.fn.expand '%:p'
|
|
local temp_dir = vim.fn.tempname()
|
|
vim.fn.mkdir(temp_dir, 'p')
|
|
local bufnr = vim.api.nvim_get_current_buf()
|
|
|
|
vim.notify('Converting PDF…', vim.log.levels.INFO)
|
|
|
|
vim.fn.jobstart({ 'pdftoppm', '-png', pdf_path, temp_dir .. '/page' }, {
|
|
on_exit = function(_, code)
|
|
vim.schedule(function()
|
|
if code ~= 0 then
|
|
vim.notify('Failed to convert PDF', vim.log.levels.ERROR)
|
|
return
|
|
end
|
|
|
|
-- Count pages
|
|
local handle = vim.loop.fs_scandir(temp_dir)
|
|
local page_count = 0
|
|
if handle then
|
|
while true do
|
|
local name = vim.loop.fs_scandir_next(handle)
|
|
if not name then
|
|
break
|
|
end
|
|
if name:match '^page%-%d+%.png$' then
|
|
page_count = page_count + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
pdf_state[bufnr] = { temp_dir = temp_dir, current_page = 1, total_pages = page_count }
|
|
|
|
local function show_page(page_num)
|
|
local state = pdf_state[bufnr]
|
|
if not state then
|
|
return
|
|
end
|
|
image.clear()
|
|
vim.bo[bufnr].modifiable = true
|
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { '' })
|
|
vim.bo[bufnr].modifiable = false
|
|
local page_file = string.format('%s/page-%d.png', state.temp_dir, page_num)
|
|
local img = image.from_file(page_file, { buffer = bufnr })
|
|
if img then
|
|
img:render()
|
|
vim.notify(string.format('Page %d / %d', page_num, state.total_pages), vim.log.levels.INFO)
|
|
else
|
|
vim.notify('Failed to load page ' .. page_num, vim.log.levels.ERROR)
|
|
end
|
|
end
|
|
|
|
local function change_page(delta)
|
|
local state = pdf_state[bufnr]
|
|
if not state then
|
|
return
|
|
end
|
|
local new_page = state.current_page + delta
|
|
if new_page < 1 or new_page > state.total_pages then
|
|
return
|
|
end
|
|
state.current_page = new_page
|
|
show_page(new_page)
|
|
end
|
|
|
|
-- Buffer-local navigation keys
|
|
local map = function(lhs, fn, desc)
|
|
vim.keymap.set('n', lhs, fn, { buffer = bufnr, desc = desc })
|
|
end
|
|
map('j', function()
|
|
change_page(1)
|
|
end, 'PDF: next page')
|
|
map('k', function()
|
|
change_page(-1)
|
|
end, 'PDF: previous page')
|
|
map('<Right>', function()
|
|
change_page(1)
|
|
end, 'PDF: next page')
|
|
map('<Left>', function()
|
|
change_page(-1)
|
|
end, 'PDF: previous page')
|
|
map('q', '<cmd>bd!<cr>', 'PDF: close')
|
|
|
|
show_page(1)
|
|
end)
|
|
end,
|
|
})
|
|
end,
|
|
})
|
|
|
|
-- Cleanup temp files on buffer close
|
|
vim.api.nvim_create_autocmd('BufDelete', {
|
|
pattern = '*.pdf',
|
|
callback = function(args)
|
|
if pdf_state[args.buf] then
|
|
image.clear()
|
|
vim.fn.delete(pdf_state[args.buf].temp_dir, 'rf')
|
|
pdf_state[args.buf] = nil
|
|
end
|
|
end,
|
|
})
|
|
end,
|
|
}
|