mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-10 13:01:29 +00:00
M12: a portfolio's initial document opens only while the portfolio is still in front; doc comments for the portfolio code and tests (#1652)
This commit is contained in:
1 parent
751720732a
commit
d9127da2e0
6 files changed
+45
-5
No files matched your search
@@ -4,6 +4,7 @@
|
||||
use pdfcraft_automation::{Automation, Content};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
/// Call tool `name` and return its JSON result.
|
||||
fn tool(a: &mut Automation, name: &str, args: Value) -> Value {
|
||||
match a.call(name, &args).unwrap().remove(0) {
|
||||
Content::Json(value) => value,
|
||||
@@ -34,6 +35,7 @@ trailer << /Root 1 0 R >>
|
||||
.into_bytes()
|
||||
}
|
||||
|
||||
/// `doc_info` names a portfolio's initial document and view, and reports none for a plain file.
|
||||
#[test]
|
||||
fn doc_info_names_the_document_a_portfolio_presents_first() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
|
||||
@@ -161,6 +161,8 @@ impl<'a> Inspector<'a> {
|
||||
me
|
||||
}
|
||||
|
||||
/// Read the document's catalog and pages into `info`: metadata, outline, fields, layers,
|
||||
/// fonts, attachments, a portfolio and where it opens.
|
||||
pub(super) fn fill(&self, info: &mut DocInfo) {
|
||||
// lopdf drops /Encrypt from the trailer once it has decrypted the file.
|
||||
info.encrypted = self.doc.encrypted();
|
||||
@@ -289,6 +291,7 @@ impl<'a> Inspector<'a> {
|
||||
Portfolio { initial, view }
|
||||
}
|
||||
|
||||
/// The object a reference points at, or `o` itself for a direct object or a broken reference.
|
||||
pub(super) fn resolve(&self, o: &'a Object) -> &'a Object {
|
||||
self.doc.dereference(o).map(|(_, o)| o).unwrap_or(o)
|
||||
}
|
||||
|
||||
@@ -26,10 +26,13 @@ trailer << /Root 1 0 R >>
|
||||
.into_bytes()
|
||||
}
|
||||
|
||||
/// The portfolio [`portfolio`] with `collection` reads as.
|
||||
fn read(collection: Option<&str>) -> Option<Portfolio> {
|
||||
inspect(Arc::new(portfolio(collection)), None).expect("opens").portfolio
|
||||
}
|
||||
|
||||
/// `/D` picks the attachment presented first, matched as the keys decode, and `/View` how the
|
||||
/// list first shows.
|
||||
#[test]
|
||||
fn a_portfolio_presents_the_attachment_its_d_names() {
|
||||
// `/D` names `b.pdf` in the keys' own UTF-16, then as a literal string: both decode as the
|
||||
|
||||
@@ -247,6 +247,8 @@ impl PdfCraftApp {
|
||||
self.finish_open(name, path, bytes.len(), prepared, activate).map(|_| ())
|
||||
}
|
||||
|
||||
/// Admit a prepared document: its tab, the panels and view it opens with, a portfolio's file
|
||||
/// list and initial document, the recent-files entry and what its scripts said on opening.
|
||||
fn finish_open(
|
||||
&mut self,
|
||||
name: &str,
|
||||
@@ -316,9 +318,9 @@ impl PdfCraftApp {
|
||||
if opening_mode != Mode::Read && self.lists_files(id) {
|
||||
self.show_portfolio_files(id, right_before);
|
||||
}
|
||||
if let Some(index) = portfolio_initial {
|
||||
self.portfolio_initial = Some((id, index));
|
||||
}
|
||||
// A document opening in front of a portfolio that hasn't presented its initial
|
||||
// document yet (files opened together) keeps the front.
|
||||
self.portfolio_initial = portfolio_initial.map(|index| (id, index));
|
||||
}
|
||||
if activate
|
||||
&& self.left_open
|
||||
@@ -428,7 +430,9 @@ impl PdfCraftApp {
|
||||
/// Leaving the tab gives back the panel the list replaced, unless the reader changed panels
|
||||
/// meanwhile. Called every frame.
|
||||
pub(crate) fn present_portfolios(&mut self) {
|
||||
if let Some((doc, index)) = self.portfolio_initial.take() {
|
||||
if let Some((doc, index)) = self.portfolio_initial.take()
|
||||
&& self.active_ids().is_some_and(|(_, id)| id == doc)
|
||||
{
|
||||
self.attachment_action(doc, index, true);
|
||||
// A portfolio inside the portfolio waits until the reader opens it, so a nested
|
||||
// file can't open tab after tab on its own.
|
||||
|
||||
@@ -269,7 +269,8 @@ pub struct PdfCraftApp {
|
||||
/// showing its comments or bookmarks (#1590). Remembered across restarts.
|
||||
pub right_panel_closed: bool,
|
||||
/// A portfolio that just opened and the attachment it presents first (`/Collection /D`),
|
||||
/// opened by [`Self::present_portfolios`] on the next frame.
|
||||
/// opened by [`Self::present_portfolios`] on the next frame while the portfolio is still the
|
||||
/// active tab.
|
||||
portfolio_initial: Option<(DocId, usize)>,
|
||||
/// The document whose tab [`Self::present_portfolios`] last saw active.
|
||||
portfolio_seen: Option<DocId>,
|
||||
|
||||
@@ -78,6 +78,8 @@ fn select(h: &mut Harness<'static, PdfCraftApp>, index: usize) {
|
||||
h.run_steps(2);
|
||||
}
|
||||
|
||||
/// The initial document opens in front, and the portfolio's tab lists its files whatever the
|
||||
/// reader's panel preference, giving the panel back when left.
|
||||
#[test]
|
||||
fn a_portfolio_opens_its_initial_document_and_shows_its_files_with_the_panel_closed() {
|
||||
let mut h = opened(portfolio("/Collection << /D (form.pdf) /View /T >>", &[("intro.pdf", PAGE), ("form.pdf", PAGE)]), true);
|
||||
@@ -106,6 +108,7 @@ fn a_portfolio_opens_its_initial_document_and_shows_its_files_with_the_panel_clo
|
||||
assert_eq!(h.state().right, Some(RightPanel::Bookmarks));
|
||||
}
|
||||
|
||||
/// The initial document opens on its own panels, not on the portfolio's file list.
|
||||
#[test]
|
||||
fn the_initial_document_opens_without_the_portfolios_file_list() {
|
||||
// The portfolio asks for the Attachments panel too (`/PageMode /UseAttachments`), and the
|
||||
@@ -118,6 +121,28 @@ fn the_initial_document_opens_without_the_portfolios_file_list() {
|
||||
assert_eq!(h.state().right, Some(RightPanel::Attachments));
|
||||
}
|
||||
|
||||
/// A file opened together with a portfolio, after it, stays in front.
|
||||
#[test]
|
||||
fn a_file_opened_after_the_portfolio_keeps_the_front() {
|
||||
// Two files opened at once, the portfolio first: the second stays in front, and the
|
||||
// portfolio's initial document waits for the reader, whose list shows on the portfolio's tab.
|
||||
let pdf = portfolio("/Collection << /D (form.pdf) >>", &[("form.pdf", PAGE)]);
|
||||
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| {
|
||||
let mut app = PdfCraftApp::new();
|
||||
app.set_option("language", "en").unwrap();
|
||||
app.right_panel_closed = true;
|
||||
app.open_bytes("portfolio.pdf", None, pdf.clone()).unwrap();
|
||||
app.open_bytes("other.pdf", None, PAGE.to_vec()).unwrap();
|
||||
app
|
||||
});
|
||||
h.run_steps(4);
|
||||
assert_eq!(tabs(&h), (vec!["portfolio.pdf".into(), "other.pdf".into()], Some("other.pdf".into())));
|
||||
assert_eq!(h.state().right, None);
|
||||
select(&mut h, 0);
|
||||
assert_eq!(h.state().right, Some(RightPanel::Attachments));
|
||||
}
|
||||
|
||||
/// `/View /H` presents the initial document but leaves the file list to the reader.
|
||||
#[test]
|
||||
fn a_portfolio_that_keeps_its_list_hidden_still_presents_its_initial_document() {
|
||||
let mut h = opened(portfolio("/Collection << /D (form.pdf) /View /H >>", &[("intro.pdf", PAGE), ("form.pdf", PAGE)]), true);
|
||||
@@ -126,6 +151,7 @@ fn a_portfolio_that_keeps_its_list_hidden_still_presents_its_initial_document()
|
||||
assert_eq!(h.state().right, None, "/View /H: the list shows when the reader asks for it");
|
||||
}
|
||||
|
||||
/// Without a PDF to present, the portfolio stays in front with its files listed.
|
||||
#[test]
|
||||
fn a_portfolio_without_an_initial_document_shows_its_files_over_the_cover() {
|
||||
// Without `/D` the cover is the initial document; a `/D` naming a file that isn't a PDF
|
||||
@@ -137,6 +163,7 @@ fn a_portfolio_without_an_initial_document_shows_its_files_over_the_cover() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A portfolio nested in the initial document doesn't open a chain of tabs.
|
||||
#[test]
|
||||
fn a_portfolio_inside_the_initial_document_waits_for_the_reader() {
|
||||
// The initial document is itself a portfolio presenting a third file: opening the outer
|
||||
|
||||
Reference in new issue
Block a user