mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-18 11:58:00 +00:00
Added the page jump for Foxit PDF Reader and Foxit PDF Editor on Windows (#1016)
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Sync Flatpak repo (push) Blocked by required conditions
Build and Release / Collect Flatpak artifacts (push) Blocked by required conditions
Build and Release / Verify (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Sync Flatpak repo (push) Blocked by required conditions
Build and Release / Collect Flatpak artifacts (push) Blocked by required conditions
Build and Release / Verify (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
This commit is contained in:
1 parent
553e02feb3
commit
9f102728b5
1 file changed
+55
-8
@@ -557,9 +557,10 @@ async fn try_open_at_page(_path: &Path, _page: u32) -> bool {
|
|||||||
/// They all mean the same thing and every one of them spells it differently. A viewer which is not
|
/// They all mean the same thing and every one of them spells it differently. A viewer which is not
|
||||||
/// covered here shows its first page, which is what the system would have done anyway.
|
/// covered here shows its first page, which is what the system would have done anyway.
|
||||||
///
|
///
|
||||||
/// Which spellings exist follows from where a viewer is found: Acrobat is named by the Windows
|
/// Which spellings exist follows from where a viewer is found: Acrobat and Foxit are named by the
|
||||||
/// registration and by nothing else, and the three Linux viewers are named by a desktop entry and
|
/// Windows registration and by nothing else, and the three Linux viewers are named by a desktop
|
||||||
/// by nothing else. Only a browser is reached on both, so only its spelling is needed everywhere.
|
/// entry and by nothing else. Only a browser is reached on both, so only its spelling is needed
|
||||||
|
/// everywhere.
|
||||||
#[cfg(any(windows, target_os = "linux", test))]
|
#[cfg(any(windows, target_os = "linux", test))]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
enum PageArgument {
|
enum PageArgument {
|
||||||
@@ -571,6 +572,11 @@ enum PageArgument {
|
|||||||
#[cfg(any(windows, test))]
|
#[cfg(any(windows, test))]
|
||||||
AcrobatOpenAction,
|
AcrobatOpenAction,
|
||||||
|
|
||||||
|
/// Foxit PDF Reader and Foxit PDF Editor take the same open action, but after the file:
|
||||||
|
/// `handbook.pdf /A page=12`.
|
||||||
|
#[cfg(any(windows, test))]
|
||||||
|
FoxitOpenAction,
|
||||||
|
|
||||||
/// The GNOME document viewer and its forks count from zero, so page 12 is index 11.
|
/// The GNOME document viewer and its forks count from zero, so page 12 is index 11.
|
||||||
#[cfg(any(target_os = "linux", test))]
|
#[cfg(any(target_os = "linux", test))]
|
||||||
ZeroBasedIndex,
|
ZeroBasedIndex,
|
||||||
@@ -615,6 +621,9 @@ fn page_arguments(argument: PageArgument, path: &Path, page: u32) -> Option<Vec<
|
|||||||
#[cfg(any(windows, test))]
|
#[cfg(any(windows, test))]
|
||||||
PageArgument::AcrobatOpenAction => vec![String::from("/A"), format!("page={page}"), path_argument],
|
PageArgument::AcrobatOpenAction => vec![String::from("/A"), format!("page={page}"), path_argument],
|
||||||
|
|
||||||
|
#[cfg(any(windows, test))]
|
||||||
|
PageArgument::FoxitOpenAction => vec![path_argument, String::from("/A"), format!("page={page}")],
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", test))]
|
#[cfg(any(target_os = "linux", test))]
|
||||||
PageArgument::ZeroBasedIndex => vec![format!("--page-index={}", page.saturating_sub(1)), path_argument],
|
PageArgument::ZeroBasedIndex => vec![format!("--page-index={}", page.saturating_sub(1)), path_argument],
|
||||||
|
|
||||||
@@ -667,6 +676,7 @@ async fn resolve_document_open_plan(path: &Path, page: u32) -> DocumentOpenPlan
|
|||||||
};
|
};
|
||||||
|
|
||||||
let Some(argument) = windows_page_argument(&prog_id) else {
|
let Some(argument) = windows_page_argument(&prog_id) else {
|
||||||
|
info!("The program registered for PDFs as '{prog_id}' cannot be sent to a page; the document opens on its first page.");
|
||||||
return DocumentOpenPlan::Plain;
|
return DocumentOpenPlan::Plain;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -688,6 +698,7 @@ async fn resolve_document_open_plan(path: &Path, page: u32) -> DocumentOpenPlan
|
|||||||
};
|
};
|
||||||
|
|
||||||
let Some((program, argument)) = linux_page_aware_program(&desktop_id) else {
|
let Some((program, argument)) = linux_page_aware_program(&desktop_id) else {
|
||||||
|
info!("The program registered for PDFs as '{desktop_id}' cannot be sent to a page; the document opens on its first page.");
|
||||||
return DocumentOpenPlan::Plain;
|
return DocumentOpenPlan::Plain;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -703,14 +714,26 @@ async fn resolve_document_open_plan(path: &Path, page: u32) -> DocumentOpenPlan
|
|||||||
///
|
///
|
||||||
/// The user's own choice comes first; the class registration is what is left when they never made
|
/// The user's own choice comes first; the class registration is what is left when they never made
|
||||||
/// one, for instance right after the system was installed.
|
/// one, for instance right after the system was installed.
|
||||||
|
///
|
||||||
|
/// Newer builds of Windows 11 keep that choice under `UserChoiceLatest` as well and, once they have
|
||||||
|
/// moved it there, go by nothing else. Whether `UserChoice` still follows a later change is nowhere
|
||||||
|
/// documented, so the newer key is asked first and the older one only where it is missing.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn windows_default_pdf_prog_id() -> Option<String> {
|
fn windows_default_pdf_prog_id() -> Option<String> {
|
||||||
use windows_registry::*;
|
use windows_registry::*;
|
||||||
|
|
||||||
const USER_CHOICE_KEY: &str = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.pdf\UserChoice";
|
//
|
||||||
|
// Not a typo: below `UserChoiceLatest`, the value `ProgId` sits in a key named `ProgId`.
|
||||||
|
//
|
||||||
|
const USER_CHOICE_KEYS: [&str; 2] = [
|
||||||
|
r"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.pdf\UserChoiceLatest\ProgId",
|
||||||
|
r"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.pdf\UserChoice",
|
||||||
|
];
|
||||||
|
|
||||||
if let Ok(key) = CURRENT_USER.open(USER_CHOICE_KEY) && let Ok(prog_id) = key.get_string("ProgId") {
|
for user_choice_key in USER_CHOICE_KEYS {
|
||||||
return Some(prog_id);
|
if let Ok(key) = CURRENT_USER.open(user_choice_key) && let Ok(prog_id) = key.get_string("ProgId") && !prog_id.is_empty() {
|
||||||
|
return Some(prog_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CLASSES_ROOT.open(".pdf").ok()
|
CLASSES_ROOT.open(".pdf").ok()
|
||||||
@@ -756,13 +779,21 @@ fn windows_page_argument(prog_id: &str) -> Option<PageArgument> {
|
|||||||
let prog_id = prog_id.to_ascii_lowercase();
|
let prog_id = prog_id.to_ascii_lowercase();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Acrobat is asked about first, because its registration says nothing about a browser while
|
// Acrobat and Foxit are asked about first, because their registration says nothing about a
|
||||||
// the browsers below are recognized by their own name in it.
|
// browser while the browsers below are recognized by their own name in it.
|
||||||
//
|
//
|
||||||
if prog_id.contains("acroexch") || prog_id.contains("acrobat") {
|
if prog_id.contains("acroexch") || prog_id.contains("acrobat") {
|
||||||
return Some(PageArgument::AcrobatOpenAction);
|
return Some(PageArgument::AcrobatOpenAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Foxit keeps its names across products: the current Foxit PDF Editor still registers as
|
||||||
|
// `FoxitPhantomPDF.Document`, so asking for the maker covers the Reader and the Editor alike.
|
||||||
|
//
|
||||||
|
if prog_id.contains("foxit") {
|
||||||
|
return Some(PageArgument::FoxitOpenAction);
|
||||||
|
}
|
||||||
|
|
||||||
const BROWSERS: [&str; 5] = ["msedge", "chrome", "firefox", "opera", "brave"];
|
const BROWSERS: [&str; 5] = ["msedge", "chrome", "firefox", "opera", "brave"];
|
||||||
if BROWSERS.iter().any(|browser| prog_id.contains(browser)) {
|
if BROWSERS.iter().any(|browser| prog_id.contains(browser)) {
|
||||||
return Some(PageArgument::UrlFragment);
|
return Some(PageArgument::UrlFragment);
|
||||||
@@ -1194,6 +1225,12 @@ mod tests {
|
|||||||
vec![String::from("/A"), String::from("page=12"), String::from("/docs/handbook.pdf")],
|
vec![String::from("/A"), String::from("page=12"), String::from("/docs/handbook.pdf")],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
page_arguments(PageArgument::FoxitOpenAction, document, 12).unwrap(),
|
||||||
|
vec![String::from("/docs/handbook.pdf"), String::from("/A"), String::from("page=12")],
|
||||||
|
"Foxit takes the open action Acrobat takes, but only after the file.",
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
page_arguments(PageArgument::ZeroBasedIndex, document, 12).unwrap(),
|
page_arguments(PageArgument::ZeroBasedIndex, document, 12).unwrap(),
|
||||||
vec![String::from("--page-index=11"), String::from("/docs/handbook.pdf")],
|
vec![String::from("--page-index=11"), String::from("/docs/handbook.pdf")],
|
||||||
@@ -1214,6 +1251,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn windows_recognizes_the_programs_it_can_send_to_a_page() {
|
fn windows_recognizes_the_programs_it_can_send_to_a_page() {
|
||||||
assert_eq!(windows_page_argument("AcroExch.Document.DC"), Some(PageArgument::AcrobatOpenAction));
|
assert_eq!(windows_page_argument("AcroExch.Document.DC"), Some(PageArgument::AcrobatOpenAction));
|
||||||
|
assert_eq!(windows_page_argument("FoxitPhantomPDF.Document"), Some(PageArgument::FoxitOpenAction), "The current Foxit PDF Editor still registers under its old name.");
|
||||||
|
assert_eq!(windows_page_argument("FoxitPDFEditor.Document"), Some(PageArgument::FoxitOpenAction));
|
||||||
|
assert_eq!(windows_page_argument("FoxitReader.Document"), Some(PageArgument::FoxitOpenAction));
|
||||||
|
assert_eq!(windows_page_argument("Applications\\FoxitPDFReader.exe"), Some(PageArgument::FoxitOpenAction), "A program picked through 'Open with' is registered by its file name.");
|
||||||
assert_eq!(windows_page_argument("MSEdgePDF"), Some(PageArgument::UrlFragment));
|
assert_eq!(windows_page_argument("MSEdgePDF"), Some(PageArgument::UrlFragment));
|
||||||
assert_eq!(windows_page_argument("ChromePDF"), Some(PageArgument::UrlFragment));
|
assert_eq!(windows_page_argument("ChromePDF"), Some(PageArgument::UrlFragment));
|
||||||
assert_eq!(windows_page_argument("FirefoxPDF"), Some(PageArgument::UrlFragment));
|
assert_eq!(windows_page_argument("FirefoxPDF"), Some(PageArgument::UrlFragment));
|
||||||
@@ -1228,6 +1269,12 @@ mod tests {
|
|||||||
"A quoted program keeps the spaces in its path and loses the arguments written for a file name.",
|
"A quoted program keeps the spaces in its path and loses the arguments written for a file name.",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
executable_from_command(r#""c:\Program Files (x86)\Foxit Software\Foxit PDF Editor\FoxitPDFEditor.exe" "%1""#).as_deref(),
|
||||||
|
Some(r"c:\Program Files (x86)\Foxit Software\Foxit PDF Editor\FoxitPDFEditor.exe"),
|
||||||
|
"The file name the command names is dropped, because the page arguments name the file themselves.",
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
executable_from_command(r#"C:\Windows\System32\viewer.exe "%1""#).as_deref(),
|
executable_from_command(r#"C:\Windows\System32\viewer.exe "%1""#).as_deref(),
|
||||||
Some(r"C:\Windows\System32\viewer.exe"),
|
Some(r"C:\Windows\System32\viewer.exe"),
|
||||||
|
|||||||
Reference in new issue
Block a user