refactor(pdf): rename crate to pdf_viewer and enforce repo conventions
This commit is contained in:
1 parent
93ed1b74c1
commit
23e07e06f4
21 files changed
+114
-214
No files matched your search
Generated
+16
-16
@@ -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",
|
||||
|
||||
+2
-2
@@ -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" }
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
> [!IMPORTANT]
|
||||
> Remove this line to confirm you've reviewed this PR before submitting.
|
||||
|
||||
# Zed
|
||||
|
||||
[](https://zed.dev)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<usize> {
|
||||
@@ -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,
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
//! User interface components for PDF viewing.
|
||||
|
||||
pub mod toolbar;
|
||||
|
||||
pub use toolbar::{PdfToolbarAction, PdfToolbarState};
|
||||
@@ -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
|
||||
@@ -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
|
||||
File renamed without changes.
File renamed without changes.
@@ -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
|
||||
|
||||
@@ -496,6 +496,25 @@ pub struct PdfDocumentRenderResult {
|
||||
pub pages: Vec<PdfPageRenderResult>,
|
||||
}
|
||||
|
||||
#[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
|
||||
File renamed without changes.
File renamed without changes.
@@ -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.
|
||||
|
||||
File renamed without changes.
File renamed without changes.
+1
-1
@@ -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";
|
||||
@@ -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
|
||||
|
||||
@@ -122,8 +122,8 @@ pub struct PdfPageEntry {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub image: Option<Arc<gpui::RenderImage>>,
|
||||
pub links: Vec<kkpdf_zed::PdfLinkAnnotation>,
|
||||
pub text_segments: Vec<kkpdf_zed::PdfTextSegment>,
|
||||
pub links: Vec<pdf_viewer::PdfLinkAnnotation>,
|
||||
pub text_segments: Vec<pdf_viewer::PdfTextSegment>,
|
||||
pub page_text: String,
|
||||
}
|
||||
|
||||
@@ -178,9 +178,9 @@ pub fn project_image_source(project: WeakEntity<Project>, path: ProjectPath) ->
|
||||
impl ImageItem {
|
||||
pub fn compute_metadata_from_bytes(image_bytes: &[u8]) -> Result<ImageMetadata> {
|
||||
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<PdfPageEntry>, Option<String>)> {
|
||||
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() {
|
||||
|
||||
@@ -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<T: ScrollableHandle> Element for ScrollbarElement<T> {
|
||||
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<T: ScrollableHandle> Element for ScrollbarElement<T> {
|
||||
// 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<T: ScrollableHandle> Element for ScrollbarElement<T> {
|
||||
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(),
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user