From 6532666c56b195117a57010d5ada06e277768ca0 Mon Sep 17 00:00:00 2001 From: Chris Cochrun Date: Fri, 12 Sep 2025 16:40:31 -0500 Subject: [PATCH] starting to add pdfviewer --- src/ui/mod.rs | 1 + src/ui/pdf.rs | 143 ++++++++++++++++++++++++++++++++++++++++++++ src/ui/presenter.rs | 4 +- 3 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 src/ui/pdf.rs diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 8372371..4719bc0 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -2,6 +2,7 @@ use crate::core::model::LibraryKind; pub mod double_ended_slider; pub mod library; +pub mod pdf; pub mod presenter; pub mod slide_editor; pub mod song_editor; diff --git a/src/ui/pdf.rs b/src/ui/pdf.rs new file mode 100644 index 0000000..392caca --- /dev/null +++ b/src/ui/pdf.rs @@ -0,0 +1,143 @@ +use std::path::Path; + +use cosmic::iced::ContentFit; +use cosmic::iced::Length; +use cosmic::widget::image; +use cosmic::widget::image::Handle; +use cosmic::Element; +use miette::IntoDiagnostic; +use miette::Result; +use miette::Severity; +use mupdf::Colorspace; +use mupdf::Document; +use mupdf::Matrix; +use mupdf::Page; + +#[derive(Debug, Clone)] +pub struct PdfViewer { + document: Option, + pages: Option>, + current_page: Option, + current_index: usize, + handle: Option, +} + +enum Message {} + +impl PdfViewer { + pub fn with_pdf(pdf: impl AsRef) -> Result { + let pdf_path = pdf.as_ref(); + let document = Document::open(pdf_path).into_diagnostic()?; + let pages = document.pages().into_diagnostic()?; + let pages: Vec = + pages.filter_map(|page| page.ok()).collect(); + let Some(page) = document.pages().into_diagnostic()?.next() + else { + return Err(miette::miette!( + severity = Severity::Warning, + "There isn't a first page here" + )); + }; + let page = page.into_diagnostic()?; + let matrix = Matrix::IDENTITY; + let colorspace = Colorspace::device_rgb(); + let pixmap = page + .to_pixmap(&matrix, &colorspace, true, true) + .into_diagnostic()?; + let handle = pixmap.samples().to_vec(); + Ok(Self { + document: Some(document), + pages: Some(pages), + current_page: Some(page), + current_index: 0, + handle: Some(Handle::from_bytes(handle)), + }) + } + + pub fn insert_pdf( + &mut self, + pdf: impl AsRef, + ) -> Result<()> { + let pdf_path = pdf.as_ref(); + let document = Document::open(pdf_path).into_diagnostic()?; + let pages = document.pages().into_diagnostic()?; + let pages: Vec = + pages.filter_map(|page| page.ok()).collect(); + let Some(page) = document.pages().into_diagnostic()?.next() + else { + return Err(miette::miette!( + severity = Severity::Warning, + "There isn't a first page here" + )); + }; + let page = page.into_diagnostic()?; + let matrix = Matrix::IDENTITY; + let colorspace = Colorspace::device_rgb(); + let pixmap = page + .to_pixmap(&matrix, &colorspace, true, true) + .into_diagnostic()?; + let handle = pixmap.samples().to_vec(); + self.document = Some(document); + self.pages = Some(pages); + self.current_page = Some(page); + self.current_index = 0; + self.handle = Some(Handle::from_bytes(handle)); + Ok(()) + } + + pub fn next_page(&mut self) -> Result<()> { + let Some(ref pages) = self.pages else { + return Err(miette::miette!("No pages in doc")); + }; + let Some(page) = pages.get(self.current_index + 1) else { + return Err(miette::miette!("There isn't a next page")); + }; + let matrix = Matrix::IDENTITY; + let colorspace = Colorspace::device_rgb(); + let pixmap = page + .to_pixmap(&matrix, &colorspace, true, true) + .into_diagnostic()?; + let handle = pixmap.samples().to_vec(); + self.current_page = Some(page.to_owned()); + self.current_index += 1; + self.handle = Some(Handle::from_bytes(handle)); + Ok(()) + } + + pub fn previous_page(&mut self) -> Result<()> { + if self.current_index == 0 { + return Err(miette::miette!("You are at the first page")); + } + let Some(ref pages) = self.pages else { + return Err(miette::miette!("No pages in doc")); + }; + let Some(page) = pages.get(self.current_index - 1) else { + return Err(miette::miette!( + "There isn't a previous page" + )); + }; + let matrix = Matrix::IDENTITY; + let colorspace = Colorspace::device_rgb(); + let pixmap = page + .to_pixmap(&matrix, &colorspace, true, true) + .into_diagnostic()?; + let handle = pixmap.samples().to_vec(); + self.current_page = Some(page.to_owned()); + self.current_index -= 1; + self.handle = Some(Handle::from_bytes(handle)); + Ok(()) + } + + pub fn view(&self) -> Option> { + let Some(handle) = self.handle.clone() else { + return None; + }; + Some( + image(handle) + .width(Length::Fill) + .height(Length::Fill) + .content_fit(ContentFit::Contain) + .into(), + ) + } +} diff --git a/src/ui/presenter.rs b/src/ui/presenter.rs index 0df1184..a521374 100644 --- a/src/ui/presenter.rs +++ b/src/ui/presenter.rs @@ -30,10 +30,11 @@ use url::Url; use crate::{ core::{service_items::ServiceItem, slide::Slide}, - ui::text_svg, BackgroundKind, }; +use crate::ui::pdf::PdfViewer; + const REFERENCE_WIDTH: f32 = 1920.0; const REFERENCE_HEIGHT: f32 = 1080.0; @@ -52,6 +53,7 @@ pub(crate) struct Presenter { hovered_slide: Option<(usize, usize)>, scroll_id: Id, current_font: Font, + pdf_viewer: PdfViewer, } pub(crate) enum Action {