From 23e07e06f457bae23f22ac266a8b921ef6da031d Mon Sep 17 00:00:00 2001 From: Kushagra Kumar Date: Wed, 2 Sep 2026 21:31:12 +0530 Subject: [PATCH] refactor(pdf): rename crate to pdf_viewer and enforce repo conventions --- Cargo.lock | 32 ++-- Cargo.toml | 4 +- README.md | 3 + crates/image_viewer/Cargo.toml | 2 +- crates/image_viewer/src/image_viewer.rs | 10 +- crates/kkpdf_zed/src/ui/mod.rs | 5 - crates/{kkpdf_zed => pdf_viewer}/Cargo.toml | 6 +- crates/{kkpdf_zed => pdf_viewer}/README.md | 63 ++++---- crates/{kkpdf_zed => pdf_viewer}/src/cache.rs | 0 .../{kkpdf_zed => pdf_viewer}/src/document.rs | 0 .../lib.rs => pdf_viewer/src/pdf_viewer.rs} | 12 +- .../{kkpdf_zed => pdf_viewer}/src/pdfium.rs | 153 ++++-------------- .../src/rasterizer.rs | 0 .../{kkpdf_zed => pdf_viewer}/src/settings.rs | 0 .../ui/toolbar.rs => pdf_viewer/src/ui.rs} | 2 +- crates/{kkpdf_zed => pdf_viewer}/src/view.rs | 0 .../{kkpdf_zed => pdf_viewer}/src/watcher.rs | 0 .../tests/integration_tests.rs | 2 +- crates/project/Cargo.toml | 2 +- crates/project/src/image_store.rs | 24 +-- crates/ui/src/components/scrollbar.rs | 8 +- 21 files changed, 114 insertions(+), 214 deletions(-) delete mode 100644 crates/kkpdf_zed/src/ui/mod.rs rename crates/{kkpdf_zed => pdf_viewer}/Cargo.toml (84%) rename crates/{kkpdf_zed => pdf_viewer}/README.md (52%) rename crates/{kkpdf_zed => pdf_viewer}/src/cache.rs (100%) rename crates/{kkpdf_zed => pdf_viewer}/src/document.rs (100%) rename crates/{kkpdf_zed/src/lib.rs => pdf_viewer/src/pdf_viewer.rs} (73%) rename crates/{kkpdf_zed => pdf_viewer}/src/pdfium.rs (87%) rename crates/{kkpdf_zed => pdf_viewer}/src/rasterizer.rs (100%) rename crates/{kkpdf_zed => pdf_viewer}/src/settings.rs (100%) rename crates/{kkpdf_zed/src/ui/toolbar.rs => pdf_viewer/src/ui.rs} (97%) rename crates/{kkpdf_zed => pdf_viewer}/src/view.rs (100%) rename crates/{kkpdf_zed => pdf_viewer}/src/watcher.rs (100%) rename crates/{kkpdf_zed => pdf_viewer}/tests/integration_tests.rs (97%) diff --git a/Cargo.lock b/Cargo.lock index 7a5917c7d7..2409e06f3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8760,10 +8760,10 @@ dependencies = [ "fs", "gpui", "image", - "kkpdf-zed", "language", "log", "menu", + "pdf_viewer", "project", "settings", "theme", @@ -9446,20 +9446,6 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" -[[package]] -name = "kkpdf-zed" -version = "0.1.0" -dependencies = [ - "anyhow", - "image", - "log", - "parking_lot", - "pdfium-render", - "serde", - "serde_json", - "tempfile", -] - [[package]] name = "kqueue" version = "1.2.0" @@ -12740,6 +12726,20 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0008e816fcdaf229cdd540e9b6ca2dc4a10d65c31624abb546c6420a02846e61" +[[package]] +name = "pdf_viewer" +version = "0.1.0" +dependencies = [ + "anyhow", + "image", + "log", + "parking_lot", + "pdfium-render", + "serde", + "serde_json", + "tempfile", +] + [[package]] name = "pdfium-render" version = "0.8.37" @@ -13959,7 +13959,6 @@ dependencies = [ "image", "indexmap 2.14.0", "itertools 0.14.0", - "kkpdf-zed", "language", "log", "lsp", @@ -13968,6 +13967,7 @@ dependencies = [ "parking_lot", "path", "paths", + "pdf_viewer", "percent-encoding", "postage", "prettier", diff --git a/Cargo.toml b/Cargo.toml index ac9143ebdd..bda77a5137 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -114,7 +114,6 @@ members = [ "crates/journal", "crates/json_schema_store", "crates/keymap_editor", - "crates/kkpdf_zed", "crates/language", "crates/language_core", "crates/language_detection", @@ -159,6 +158,7 @@ members = [ "crates/panel", "crates/path", "crates/paths", + "crates/pdf_viewer", "crates/picker", "crates/picker_preview", "crates/platform_title_bar", @@ -377,7 +377,6 @@ http_client_tls = { path = "crates/http_client_tls" } http_proxy = { path = "crates/http_proxy" } icons = { path = "crates/icons" } image_viewer = { path = "crates/image_viewer" } -kkpdf-zed = { path = "crates/kkpdf_zed" } input_latency_ui = { path = "crates/input_latency_ui" } inspector_ui = { path = "crates/inspector_ui" } install_cli = { path = "crates/install_cli" } @@ -428,6 +427,7 @@ outline_panel = { path = "crates/outline_panel" } panel = { path = "crates/panel" } path = { path = "crates/path" } paths = { path = "crates/paths" } +pdf_viewer = { path = "crates/pdf_viewer" } perf = { path = "tooling/perf" } picker = { path = "crates/picker" } picker_preview = { path = "crates/picker_preview" } diff --git a/README.md b/README.md index 9f641fb384..24840eec03 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +> [!IMPORTANT] +> Remove this line to confirm you've reviewed this PR before submitting. + # Zed [![Zed](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/zed-industries/zed/main/assets/badge/v0.json)](https://zed.dev) diff --git a/crates/image_viewer/Cargo.toml b/crates/image_viewer/Cargo.toml index 4175db8f68..784d3de6d4 100644 --- a/crates/image_viewer/Cargo.toml +++ b/crates/image_viewer/Cargo.toml @@ -22,10 +22,10 @@ editor.workspace = true file_icons.workspace = true gpui.workspace = true image.workspace = true -kkpdf-zed.workspace = true language.workspace = true log.workspace = true menu.workspace = true +pdf_viewer.workspace = true project.workspace = true settings.workspace = true theme_settings.workspace = true diff --git a/crates/image_viewer/src/image_viewer.rs b/crates/image_viewer/src/image_viewer.rs index 3aeb80ce4a..9e06263d49 100644 --- a/crates/image_viewer/src/image_viewer.rs +++ b/crates/image_viewer/src/image_viewer.rs @@ -23,7 +23,7 @@ use project::{ ImageItem, Project, ProjectPath, git_store::GitStoreEvent, image_store::{ImageItemEvent, PdfPageEntry, PdfPageInfo}, }; -use kkpdf_zed::{ +use pdf_viewer::{ cache::{CacheKey, CacheablePage, PageLruCache, DEFAULT_MEMORY_BUDGET_BYTES}, rasterizer::RasterizerOptions, PdfiumEngine, @@ -492,7 +492,7 @@ impl ImageView { } fn find_segment_at( - segments: &[kkpdf_zed::PdfTextSegment], + segments: &[pdf_viewer::PdfTextSegment], norm_x: f32, norm_y: f32, ) -> Option { @@ -1774,7 +1774,7 @@ impl Render for ImageView { .custom_scrollbars( Scrollbars::new(ScrollAxes::Vertical) .id("pdf-scrollbar") - .style(ScrollbarStyle::Thick) + .style(ScrollbarStyle::Editor) .tracked_scroll_handle(&self.pdf_scroll_handle) .notify_content(), window, @@ -2508,7 +2508,7 @@ mod tests { #[test] fn test_pdf_collect_selection_text_multi_page() { - use kkpdf_zed::PdfTextSegment; + use pdf_viewer::PdfTextSegment; let dummy_img = Arc::new(gpui::RenderImage::new(Vec::new())); @@ -2615,7 +2615,7 @@ mod tests { #[test] fn test_find_segment_at_whitespace_rejection() { let segments = vec![ - kkpdf_zed::PdfTextSegment { + pdf_viewer::PdfTextSegment { text: "Hello".to_string(), x: 0.2, y: 0.2, diff --git a/crates/kkpdf_zed/src/ui/mod.rs b/crates/kkpdf_zed/src/ui/mod.rs deleted file mode 100644 index 61a506dacd..0000000000 --- a/crates/kkpdf_zed/src/ui/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! User interface components for PDF viewing. - -pub mod toolbar; - -pub use toolbar::{PdfToolbarAction, PdfToolbarState}; diff --git a/crates/kkpdf_zed/Cargo.toml b/crates/pdf_viewer/Cargo.toml similarity index 84% rename from crates/kkpdf_zed/Cargo.toml rename to crates/pdf_viewer/Cargo.toml index 1213944b47..fbc3a631d1 100644 --- a/crates/kkpdf_zed/Cargo.toml +++ b/crates/pdf_viewer/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kkpdf-zed" +name = "pdf_viewer" version = "0.1.0" edition.workspace = true publish.workspace = true @@ -8,6 +8,10 @@ license = "GPL-3.0-or-later" [lints] workspace = true +[lib] +path = "src/pdf_viewer.rs" +doctest = false + [dependencies] anyhow.workspace = true image.workspace = true diff --git a/crates/kkpdf_zed/README.md b/crates/pdf_viewer/README.md similarity index 52% rename from crates/kkpdf_zed/README.md rename to crates/pdf_viewer/README.md index d451785840..a1531c5184 100644 --- a/crates/kkpdf_zed/README.md +++ b/crates/pdf_viewer/README.md @@ -1,15 +1,13 @@ -# `kkpdf-zed`: Native PDF Viewer for Zed Editor +# `pdf_viewer`: Native PDF Viewer for Zed -A native, high-performance PDF viewing engine and GPUI workspace item engineered for the [Zed Editor](https://zed.dev). +A native, high performance PDF viewing engine and GPUI workspace item engineered for Zed. --- ## 0. Architectural Identity & Delivery Model > **Important**: This is a **native workspace crate**, not an installable WASM extension. -> As of current Zed versions, Zed's WebAssembly extension API (`zed_extension_api`) contains no custom UI or file preview surfaces. `kkpdf-zed` is designed as a native crate to be registered within the Zed workspace (`crates/kkpdf_viewer` or integrated upstream into `zed-industries/zed`). - -`extension.toml` in this repository is a **documentation stub** preserving metadata identity for a potential future extension surface or upstream contribution. +> As of current Zed versions, Zed's WebAssembly extension API (`zed_extension_api`) contains no custom UI or file preview surfaces. `pdf_viewer` is designed as a native crate registered within the Zed workspace (`crates/pdf_viewer`). --- @@ -17,26 +15,26 @@ A native, high-performance PDF viewing engine and GPUI workspace item engineered ``` ┌──────────────────────────────────────────────────────────────────────────────────┐ -│ KKPDF-ZED CORE PIPELINE │ +│ PDF_VIEWER CORE PIPELINE │ └──────────────────────────────────────────────────────────────────────────────────┘ │ [PDF Document on Disk / Memory Buffer] │ ▼ - [Thread-Safe Document Handle (`document.rs`)] + [Thread Safe Document Handle (`document.rs`)] │ ▼ [Async Background Worker Pool (`gpui::Task` / Threads)] │ - (Zero UI-Thread Blocking Rasterization) + (Zero UI Thread Blocking Rasterization) │ ▼ - [Memory-Budgeted LRU Cache (`cache.rs`)] + [Memory Budgeted LRU Cache (`cache.rs`)] │ - (Visible Viewport + 1-Page Margin) + (Visible Viewport + 1 Page Margin) │ ▼ - [Luminosity Tone-Mapping Engine (`ui/page.rs`)] + [Luminosity Tone Mapping Engine (`ui.rs`)] │ (Inverts White/Black; Preserves Images) │ @@ -44,28 +42,26 @@ A native, high-performance PDF viewing engine and GPUI workspace item engineered [Native GPUI Canvas / Bitmap Painter (`view.rs`)] ``` -1. **Zero UI-Thread Blocking (120 FPS Target)**: PDF parsing and rasterization never execute on GPUI's main render loop. Work is dispatched to background tasks that stream RGBA framebuffers into memory. -2. **Viewport Virtualization & LRU Caching**: Pages are rasterized on-demand for the visible viewport plus a 1-page prefetch margin. Stale or passed-by in-flight render requests are cancelled during fast scrolls to prevent thread pool starvation. -3. **Luminosity-Threshold Dark Mode**: Unlike simple blanket RGB inversion (which turns photos and charts into negative ghosts), `kkpdf-zed` implements selective tone mapping—remapping near-white canvas backgrounds to editor background tones and near-black text to theme light text, while preserving saturated color images. +1. **Zero UI Thread Blocking**: PDF parsing and rasterization never execute on GPUI's main render loop. Work is dispatched to background tasks that stream RGBA framebuffers into memory. +2. **Viewport Virtualization & LRU Caching**: Pages are rasterized on demand for the visible viewport plus a 1 page prefetch margin. Stale or passed by in flight render requests are cancelled during fast scrolls to prevent thread pool starvation. +3. **Luminosity Threshold Dark Mode**: Unlike simple blanket RGB inversion (which turns photos and charts into negative ghosts), `pdf_viewer` implements selective tone mapping, remapping near white canvas backgrounds to editor background tones and near black text to theme light text, while preserving saturated color images. 4. **Scroll & Zoom Preserving Live Reload**: Watches the underlying PDF on disk (via file system notifications) and reloads seamlessly without resetting the user's scroll percentage or zoom level. --- -## 2. Scope Boundaries (v1) +## 2. Scope Boundaries -- **Bitmap-Rendered Continuous Scroll**: Version 1 is engineered strictly as a high-fidelity, high-frame-rate read-only document viewer. -- **Excluded from v1**: - - Text selection and copying. - - In-document text search (`Ctrl+F`). +- **Continuous Scroll & Text Selection**: Supports fluid high frame rate document browsing and visual text selection. +- **Excluded from initial scope**: - Interactive PDF form field editing. -- *Rationale*: Delivering an ultra-fast, stutter-free viewing experience with zero UI locks takes precedence. Text extraction and selection layer synchronization will be introduced in subsequent milestones. +- *Rationale*: Delivering an ultra fast, stutter free viewing experience with zero UI locks takes precedence. --- ## 3. Security Posture & Threat Model -- **In-Process Parsing**: PDF rasterization is performed via Google's native C++ [Pdfium](https://pdfium.googlesource.com/pdfium/) engine linked in-process with the editor. -- **Accepted Risk in v1**: Because Pdfium runs in-process without sandboxing (e.g., without separate process IPC or WebAssembly memory isolation), malformed or malicious PDFs could theoretically trigger memory vulnerabilities in the underlying C++ library. +- **In Process Parsing**: PDF rasterization is performed via Google's native C++ [Pdfium](https://pdfium.googlesource.com/pdfium/) engine linked in process with the editor. +- **Accepted Risk**: Because Pdfium runs in process without sandboxing (for example, without separate process IPC or WebAssembly memory isolation), malformed or malicious PDFs could theoretically trigger memory vulnerabilities in the underlying C++ library. - **Mitigation & Future Hardening**: Memory budgets are strictly capped, document handles are isolated behind thread boundaries, and a future sandboxed worker process model is planned for untrusted file browsing. --- @@ -73,20 +69,21 @@ A native, high-performance PDF viewing engine and GPUI workspace item engineered ## 4. Repository Layout ``` -kkpdf-zed/ +crates/pdf_viewer/ ├── Cargo.toml # Native crate manifest -├── extension.toml # Documentation stub ├── README.md # Architecture & operational guide +├── tests/ +│ └── integration_tests.rs # End to end integration tests └── src/ - ├── lib.rs # Crate entrypoint & exports - ├── document.rs # Thread-safe PdfDocument handle + ├── pdf_viewer.rs # Crate entrypoint & exports + ├── cache.rs # Memory budgeted generic PageLruCache + ├── document.rs # Thread safe PdfDocument handle + ├── pdfium.rs # Pdfium engine binding & coordinate normalization ├── rasterizer.rs # Async background rendering pipeline - ├── cache.rs # Memory-budgeted LRU cache with eviction - ├── watcher.rs # Live-reload file watcher & scroll state + ├── settings.rs # PDF viewer configuration options + ├── ui.rs # Toolbar state & action models ├── view.rs # GPUI Render & Workspace Item implementation - └── ui/ - ├── page.rs # Bitmap painting & luminosity recolor - └── toolbar.rs # Zoom, fit-width & page jump controls + └── watcher.rs # Live reload file watcher & scroll state ``` --- @@ -95,6 +92,4 @@ kkpdf-zed/ Run unit and integration tests across the caching and rasterization engines: -```bash -cargo test -``` +cargo test -p pdf_viewer diff --git a/crates/kkpdf_zed/src/cache.rs b/crates/pdf_viewer/src/cache.rs similarity index 100% rename from crates/kkpdf_zed/src/cache.rs rename to crates/pdf_viewer/src/cache.rs diff --git a/crates/kkpdf_zed/src/document.rs b/crates/pdf_viewer/src/document.rs similarity index 100% rename from crates/kkpdf_zed/src/document.rs rename to crates/pdf_viewer/src/document.rs diff --git a/crates/kkpdf_zed/src/lib.rs b/crates/pdf_viewer/src/pdf_viewer.rs similarity index 73% rename from crates/kkpdf_zed/src/lib.rs rename to crates/pdf_viewer/src/pdf_viewer.rs index a79647cc9f..6b76e4c532 100644 --- a/crates/kkpdf_zed/src/lib.rs +++ b/crates/pdf_viewer/src/pdf_viewer.rs @@ -1,12 +1,12 @@ -//! # `kkpdf-zed` +//! # `pdf_viewer` //! -//! High-performance native PDF viewing engine and GPUI workspace item for Zed Editor. +//! High performance native PDF viewing engine and GPUI workspace item for Zed Editor. //! //! Features: -//! - Sub-millisecond viewport rendering via bounded LRU memory caching -//! - Smart luminosity-threshold dark mode tone mapping preserving saturated colors -//! - Continuous multi-page scroll, single-page, and two-page spread layouts -//! - Precision focal-point zoom (10% to 2000%) with mouse and gesture support +//! - High performance viewport rendering via bounded LRU memory caching +//! - Smart luminosity threshold dark mode tone mapping preserving saturated colors +//! - Continuous multi page scroll, single page, and two page spread layouts +//! - Precision focal point zoom (10% to 2000%) with mouse and gesture support //! - Debounced disk change watcher preserving exact scroll percentages and zoom levels //! - Native Pdfium C++ engine bindings with robust fallback support diff --git a/crates/kkpdf_zed/src/pdfium.rs b/crates/pdf_viewer/src/pdfium.rs similarity index 87% rename from crates/kkpdf_zed/src/pdfium.rs rename to crates/pdf_viewer/src/pdfium.rs index e5d172a938..987539733b 100644 --- a/crates/kkpdf_zed/src/pdfium.rs +++ b/crates/pdf_viewer/src/pdfium.rs @@ -496,6 +496,25 @@ pub struct PdfDocumentRenderResult { pub pages: Vec, } +#[inline] +fn normalize_coordinates( + left: f32, + right: f32, + bottom: f32, + top: f32, + page_w: f32, + page_h: f32, +) -> (f32, f32, f32, f32) { + if page_w <= 0.0 || page_h <= 0.0 { + return (0.0, 0.0, 0.0, 0.0); + } + let norm_x = (left / page_w).clamp(0.0, 1.0); + let norm_y = ((page_h - top) / page_h).clamp(0.0, 1.0); + let norm_w = ((right - left) / page_w).clamp(0.0, 1.0); + let norm_h = ((top - bottom) / page_h).clamp(0.0, 1.0); + (norm_x, norm_y, norm_w, norm_h) +} + fn extract_page_text_and_links( page: &pdfium_render::prelude::PdfPage, idx: usize, @@ -534,26 +553,8 @@ fn extract_page_text_and_links( let bottom = bounds.bottom().value.min(bounds.top().value); let top = bounds.bottom().value.max(bounds.top().value); - let norm_x = if page_w > 0.0 { - (left / page_w).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_y = if page_h > 0.0 { - ((page_h - top) / page_h).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_w = if page_w > 0.0 { - ((right - left) / page_w).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_h = if page_h > 0.0 { - ((top - bottom) / page_h).clamp(0.0, 1.0) - } else { - 0.0 - }; + let (norm_x, norm_y, norm_w, norm_h) = + normalize_coordinates(left, right, bottom, top, page_w, page_h); if norm_w > 0.0 && norm_h > 0.0 { segments.push(PdfTextSegment { @@ -581,26 +582,9 @@ fn extract_page_text_and_links( if is_ws { if has_word_char && !word_text.is_empty() { - let norm_x = if page_w > 0.0 { - (min_left / page_w).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_y = if page_h > 0.0 { - ((page_h - max_top) / page_h).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_w = if page_w > 0.0 { - ((max_right - min_left) / page_w).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_h = if page_h > 0.0 { - ((max_top - min_bottom) / page_h).clamp(0.0, 1.0) - } else { - 0.0 - }; + let (norm_x, norm_y, norm_w, norm_h) = normalize_coordinates( + min_left, max_right, min_bottom, max_top, page_w, page_h, + ); if norm_w > 0.0 && norm_h > 0.0 { segments.push(PdfTextSegment { @@ -636,26 +620,9 @@ fn extract_page_text_and_links( } if has_word_char && !word_text.is_empty() { - let norm_x = if page_w > 0.0 { - (min_left / page_w).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_y = if page_h > 0.0 { - ((page_h - max_top) / page_h).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_w = if page_w > 0.0 { - ((max_right - min_left) / page_w).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_h = if page_h > 0.0 { - ((max_top - min_bottom) / page_h).clamp(0.0, 1.0) - } else { - 0.0 - }; + let (norm_x, norm_y, norm_w, norm_h) = normalize_coordinates( + min_left, max_right, min_bottom, max_top, page_w, page_h, + ); if norm_w > 0.0 && norm_h > 0.0 { segments.push(PdfTextSegment { @@ -674,26 +641,8 @@ fn extract_page_text_and_links( let bottom = bounds.bottom().value.min(bounds.top().value); let top = bounds.bottom().value.max(bounds.top().value); - let norm_x = if page_w > 0.0 { - (left / page_w).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_y = if page_h > 0.0 { - ((page_h - top) / page_h).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_w = if page_w > 0.0 { - ((right - left) / page_w).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_h = if page_h > 0.0 { - ((top - bottom) / page_h).clamp(0.0, 1.0) - } else { - 0.0 - }; + let (norm_x, norm_y, norm_w, norm_h) = + normalize_coordinates(left, right, bottom, top, page_w, page_h); segments.push(PdfTextSegment { text: seg_trimmed.to_string(), @@ -718,26 +667,8 @@ fn extract_page_text_and_links( let bottom = rect.bottom().value.min(rect.top().value); let top = rect.bottom().value.max(rect.top().value); - let norm_x = if page_w > 0.0 { - (left / page_w).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_y = if page_h > 0.0 { - ((page_h - top) / page_h).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_w = if page_w > 0.0 { - ((right - left) / page_w).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_h = if page_h > 0.0 { - ((top - bottom) / page_h).clamp(0.0, 1.0) - } else { - 0.0 - }; + let (norm_x, norm_y, norm_w, norm_h) = + normalize_coordinates(left, right, bottom, top, page_w, page_h); links.push(PdfLinkAnnotation { url: url_clean, @@ -766,26 +697,8 @@ fn extract_page_text_and_links( let bottom = rect.bottom().value.min(rect.top().value); let top = rect.bottom().value.max(rect.top().value); - let norm_x = if page_w > 0.0 { - (left / page_w).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_y = if page_h > 0.0 { - ((page_h - top) / page_h).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_w = if page_w > 0.0 { - ((right - left) / page_w).clamp(0.0, 1.0) - } else { - 0.0 - }; - let norm_h = if page_h > 0.0 { - ((top - bottom) / page_h).clamp(0.0, 1.0) - } else { - 0.0 - }; + let (norm_x, norm_y, norm_w, norm_h) = + normalize_coordinates(left, right, bottom, top, page_w, page_h); let exists = links.iter().any(|l| { l.url == url_clean diff --git a/crates/kkpdf_zed/src/rasterizer.rs b/crates/pdf_viewer/src/rasterizer.rs similarity index 100% rename from crates/kkpdf_zed/src/rasterizer.rs rename to crates/pdf_viewer/src/rasterizer.rs diff --git a/crates/kkpdf_zed/src/settings.rs b/crates/pdf_viewer/src/settings.rs similarity index 100% rename from crates/kkpdf_zed/src/settings.rs rename to crates/pdf_viewer/src/settings.rs diff --git a/crates/kkpdf_zed/src/ui/toolbar.rs b/crates/pdf_viewer/src/ui.rs similarity index 97% rename from crates/kkpdf_zed/src/ui/toolbar.rs rename to crates/pdf_viewer/src/ui.rs index d6f40af2c0..a09ea87a0d 100644 --- a/crates/kkpdf_zed/src/ui/toolbar.rs +++ b/crates/pdf_viewer/src/ui.rs @@ -1,4 +1,4 @@ -//! PDF viewer toolbar control models and layout definitions. +//! User interface components and toolbar state models for PDF viewing. //! //! Provides navigation, zoom presets, page jump controls, and dark mode toggles. diff --git a/crates/kkpdf_zed/src/view.rs b/crates/pdf_viewer/src/view.rs similarity index 100% rename from crates/kkpdf_zed/src/view.rs rename to crates/pdf_viewer/src/view.rs diff --git a/crates/kkpdf_zed/src/watcher.rs b/crates/pdf_viewer/src/watcher.rs similarity index 100% rename from crates/kkpdf_zed/src/watcher.rs rename to crates/pdf_viewer/src/watcher.rs diff --git a/crates/kkpdf_zed/tests/integration_tests.rs b/crates/pdf_viewer/tests/integration_tests.rs similarity index 97% rename from crates/kkpdf_zed/tests/integration_tests.rs rename to crates/pdf_viewer/tests/integration_tests.rs index c275bfca59..3ce13e6358 100644 --- a/crates/kkpdf_zed/tests/integration_tests.rs +++ b/crates/pdf_viewer/tests/integration_tests.rs @@ -1,4 +1,4 @@ -use kkpdf_zed::{PageLayoutMode, PdfToolbarState, PdfView, PdfViewerSettings}; +use pdf_viewer::{PageLayoutMode, PdfToolbarState, PdfView, PdfViewerSettings}; use std::time::Duration; const MINIMAL_PDF: &[u8] = b"%PDF-1.7\n1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>\nendobj\nxref\n0 4\n0000000000 65535 f \n0000000010 00000 n \n0000000060 00000 n \n0000000117 00000 n \ntrailer\n<< /Size 4 /Root 1 0 R >>\nstartxref\n185\n%%EOF\n"; diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index 2a1e6af097..d82f995775 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -61,7 +61,6 @@ globset.workspace = true gpui.workspace = true http_client = { workspace = true, features = ["github-download"] } image.workspace = true -kkpdf-zed.workspace = true indexmap.workspace = true itertools.workspace = true language.workspace = true @@ -72,6 +71,7 @@ node_runtime.workspace = true parking_lot.workspace = true path.workspace = true paths.workspace = true +pdf_viewer.workspace = true percent-encoding.workspace = true postage.workspace = true prettier.workspace = true diff --git a/crates/project/src/image_store.rs b/crates/project/src/image_store.rs index a194848312..93f3cc30cd 100644 --- a/crates/project/src/image_store.rs +++ b/crates/project/src/image_store.rs @@ -122,8 +122,8 @@ pub struct PdfPageEntry { pub width: u32, pub height: u32, pub image: Option>, - pub links: Vec, - pub text_segments: Vec, + pub links: Vec, + pub text_segments: Vec, pub page_text: String, } @@ -178,9 +178,9 @@ pub fn project_image_source(project: WeakEntity, path: ProjectPath) -> impl ImageItem { pub fn compute_metadata_from_bytes(image_bytes: &[u8]) -> Result { if image_bytes.starts_with(b"%PDF-") { - let engine = kkpdf_zed::pdfium::PdfiumEngine::new(); + let engine = pdf_viewer::pdfium::PdfiumEngine::new(); let doc = engine.load_document_from_bytes(image_bytes, None)?; - let dim = doc.page_size(0).unwrap_or(kkpdf_zed::PageDimensions::new(612.0, 792.0)); + let dim = doc.page_size(0).unwrap_or(pdf_viewer::PageDimensions::new(612.0, 792.0)); let (width, height) = dim.to_pixel_size(1.0, 144.0); return Ok(ImageMetadata { width, @@ -278,7 +278,7 @@ impl ImageItem { if let Some(ref text) = info.full_text { Some(text.clone()) } else { - kkpdf_zed::PdfiumEngine::new().extract_text_from_bytes(&info.pdf_bytes, None).ok() + pdf_viewer::PdfiumEngine::new().extract_text_from_bytes(&info.pdf_bytes, None).ok() } } @@ -1122,7 +1122,7 @@ impl LocalImageStore { pub fn create_gpui_images_from_pdf( pdf_bytes: &[u8], ) -> anyhow::Result<(Vec, Option)> { - let engine = kkpdf_zed::PdfiumEngine::new(); + let engine = pdf_viewer::PdfiumEngine::new(); let details = engine.extract_document_details(pdf_bytes)?; let mut pages = Vec::with_capacity(details.pages.len()); @@ -1138,16 +1138,8 @@ pub fn create_gpui_images_from_pdf( }); } - if pages.is_empty() && pdf_bytes.starts_with(b"%PDF-") { - pages.push(PdfPageEntry { - page_index: 0, - width: 612, - height: 792, - image: None, - links: Vec::new(), - text_segments: Vec::new(), - page_text: String::new(), - }); + if pages.is_empty() { + anyhow::bail!("Failed to parse PDF document: no pages found or Pdfium engine unavailable"); } let full_text = if !details.full_text.is_empty() { diff --git a/crates/ui/src/components/scrollbar.rs b/crates/ui/src/components/scrollbar.rs index 7adc068d86..e7177f4eca 100644 --- a/crates/ui/src/components/scrollbar.rs +++ b/crates/ui/src/components/scrollbar.rs @@ -351,7 +351,6 @@ pub enum ScrollbarStyle { #[default] Regular, Editor, - Thick, } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] @@ -375,7 +374,6 @@ impl ScrollbarStyle { match self { ScrollbarStyle::Regular => px(6.), ScrollbarStyle::Editor => px(15.), - ScrollbarStyle::Thick => px(14.), } } } @@ -1242,7 +1240,7 @@ impl Element for ScrollbarElement { bounds.size.apply_along(axis.invert(), |_| { width + match state.style { - ScrollbarStyle::Regular | ScrollbarStyle::Thick => 2 * SCROLLBAR_PADDING, + ScrollbarStyle::Regular => 2 * SCROLLBAR_PADDING, ScrollbarStyle::Editor => Pixels::ZERO, } }), @@ -1254,7 +1252,7 @@ impl Element for ScrollbarElement { // Rounded style needs a bit of padding, whereas for editor scrollbars, // we want the full length of the track let thumb_container_bounds = match state.style { - ScrollbarStyle::Regular | ScrollbarStyle::Thick => { + ScrollbarStyle::Regular => { scroll_track_bounds.dilate(-SCROLLBAR_PADDING) } ScrollbarStyle::Editor if has_border => scroll_track_bounds @@ -1490,7 +1488,7 @@ impl Element for ScrollbarElement { window.paint_quad(quad( *thumb_bounds, match style { - ScrollbarStyle::Regular | ScrollbarStyle::Thick => Corners::all(Pixels::MAX) + ScrollbarStyle::Regular => Corners::all(Pixels::MAX) .clamp_radii_for_quad_size(thumb_bounds.size), ScrollbarStyle::Editor => Corners::default(), },