1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
//! Wrapper around `document.h` functions acting on `zathura_document_t`.
use {
crate::{sys, PageRef},
std::{ffi::CStr, marker::PhantomData, str::Utf8Error},
};
/// A mutable reference to a Zathura document.
#[derive(Debug)]
pub struct DocumentRef<'a> {
ptr: *mut sys::zathura_document_t,
_p: PhantomData<&'a mut ()>,
}
impl DocumentRef<'_> {
/// Creates a document reference from a raw pointer.
///
/// # Safety
///
/// This method requires that `ptr` points to a valid Zathura document. The
/// returned `DocumentRef` also has a dangling lifetime, which must be
/// constrained by the caller so that it doesn't outlive the document.
///
/// Only a single `DocumentRef` may be created for the same document, since
/// it is effectively a mutable reference. A `DocumentRef` may not coexist
/// with `PageRef`s to pages in the document either, since they can be
/// mutably accessed via `page`.
pub unsafe fn from_raw(ptr: *mut sys::zathura_document_t) -> Self {
Self {
ptr,
_p: PhantomData,
}
}
/// Returns the file path as a raw C string.
///
/// If the document was loaded from a URI, this will return a temporary file
/// path.
pub fn path_raw(&self) -> &CStr {
unsafe {
let ptr = sys::zathura_document_get_path(self.ptr);
assert!(!ptr.is_null(), "`zathura_document_get_path` returned NULL");
CStr::from_ptr(ptr)
}
}
/// Returns the file path from which this document was or will be loaded.
///
/// If the document was loaded from a URI and not a local file path, this
/// will return a temporary file path. Returns a `Utf8Error` when the raw
/// path does not contain valid UTF-8.
pub fn path_utf8(&self) -> Result<&str, Utf8Error> {
self.path_raw().to_str()
}
/// Returns the URI this document was loaded from.
///
/// If the document was loaded from a local file path instead of a URI, this
/// will return `None`.
pub fn uri_raw(&self) -> Option<&CStr> {
unsafe {
let ptr = sys::zathura_document_get_uri(self.ptr);
// This isn't documented, but the URI will be NULL if the document
// was loaded from a local path
if ptr.is_null() {
None
} else {
Some(CStr::from_ptr(ptr))
}
}
}
/// Returns the URI from which this document was or will be loaded.
///
/// Returns `None` if the document was loaded from a local file path and not
/// a URI. Returns a `Utf8Error` when the raw URI does not contain valid
/// UTF-8.
pub fn uri_utf8(&self) -> Option<Result<&str, Utf8Error>> {
self.uri_raw().map(CStr::to_str)
}
/// Returns the raw basename of the document's path.
///
/// If the document was loaded from a URI, this will return the URI's
/// basename. Otherwise, this will return the basename of the local file
/// path.
pub fn basename_raw(&self) -> &CStr {
unsafe {
let ptr = sys::zathura_document_get_basename(self.ptr);
assert!(
!ptr.is_null(),
"`zathura_document_get_basename` returned NULL"
);
CStr::from_ptr(ptr)
}
}
/// Returns the UTF-8 basename of the document's path.
///
/// If the document was loaded from a URI, this will return the URI's
/// basename. Otherwise, this will return the basename of the local file
/// path.
///
/// If the basename is not valid UTF-8, a `Utf8Error` will be returned.
pub fn basename_utf8(&self) -> Result<&str, Utf8Error> {
self.basename_raw().to_str()
}
/// Returns the zoom level of the document.
///
/// The zoom level is exclusively user-controlled and does not take PPI of
/// the screen or target surface into account.
pub fn zoom(&self) -> f64 {
unsafe { sys::zathura_document_get_zoom(self.ptr) }
}
/// Returns the render scale in device pixels per point.
///
/// This takes the zoom level and device PPI into account. Note that even
/// with a zoom of `1.0` and on a non-HiDPI screen this will not return
/// `1.0`: Zathura assumes that documents use 72 points per inch, while
/// non-HiDPI monitors usually have a resolution of 90-100 pixels per inch.
pub fn scale(&self) -> f64 {
unsafe { sys::zathura_document_get_scale(self.ptr) }
}
/// Returns the viewport rotation in degrees.
///
/// Zathura only supports rotation by a multiple of 90°. Thus this value is
/// limited to the range 0-270 in multiples of 90 (ie. it can be 0, 90,
/// 180 or 270).
pub fn rotation(&self) -> u32 {
unsafe { sys::zathura_document_get_rotation(self.ptr) }
}
/// Returns the viewport's Pixels Per Inch.
///
/// This value might be 0.0 if no PPI information is available.
pub fn viewport_ppi(&self) -> f64 {
unsafe { sys::zathura_document_get_viewport_ppi(self.ptr) }
}
/// Returns the device scaling factors to map from window coordinates to
/// pixel coordinates.
///
/// Currently, Zathura uses `gtk_widget_get_scale_factor` for this, so this
/// will return integer values only, and doesn't distinguish between X and Y
/// axis, so both returned values will be the same.
///
/// On a "normal" screen, this will return `(1.0, 1.0)`, while on a HiDPI
/// screen it will return higher values (eg. `(2.0, 2.0)`). The returned
/// values will never be `0.0`.
pub fn scaling_factors(&self) -> (f64, f64) {
unsafe {
let factors = sys::zathura_document_get_device_factors(self.ptr);
(factors.x, factors.y)
}
}
/// Returns the size of a page cell in the document (in device pixels).
///
/// Since pages can vary in size, this will return the largest cell size
/// needed to fit all pages.
///
/// This takes scaling and rotation into account.
pub fn cell_size(&self) -> (u32, u32) {
unsafe {
let (mut height, mut width) = (0, 0);
sys::zathura_document_get_cell_size(self.ptr, &mut height, &mut width);
(width, height)
}
}
/// Returns the number of pages in this document.
pub fn page_count(&self) -> u32 {
unsafe { sys::zathura_document_get_number_of_pages(self.ptr) as u32 }
}
/// Obtains a mutable reference to the page at `index`.
///
/// Returns `None` when `index` is larger than or equal to the number of
/// pages in this document.
pub fn page<'a>(&'a mut self, index: usize) -> Option<PageRef<'a>> {
unsafe {
let ptr = sys::zathura_document_get_page(self.ptr, index as _);
if ptr.is_null() {
None
} else {
Some(PageRef::from_raw(ptr))
}
}
}
/// Returns the currently focused page index.
///
/// Multiple pages can be displayed at the same time, in both X and Y
/// direction. The returned index usually belongs to the "most-visible"
/// page in the viewport.
pub fn current_page_index(&self) -> u32 {
unsafe { sys::zathura_document_get_current_page_number(self.ptr) as u32 }
}
/// Unsafely sets the page count in the document to another value.
///
/// This will not allocate any pages.
///
/// # Safety
///
/// This enables UB via dangling pointer deref through methods like `page`.
/// It should only be used when the situation will be corrected immediately
/// (eg. by allocating the right number of pages).
pub unsafe fn set_page_count(&mut self, count: u32) {
sys::zathura_document_set_number_of_pages(self.ptr, count as _)
}
/// Returns the plugin-controlled pointer associated with the document.
///
/// This is mostly for internal use by this library and is usually unsafe
/// to dereference.
pub fn plugin_data(&self) -> *mut () {
unsafe { sys::zathura_document_get_data(self.ptr) as *mut () }
}
/// Sets the custom plugin data pointer to `data`.
///
/// # Safety
///
/// This method is unsafe and should not be used by plugins. Instead, the
/// `ZathuraPlugin` trait already provides an associated `DocumentData`
/// type, which can be used instead.
///
/// This library will assume that the plugin data is a
/// `*mut Plugin::DocumentData` obtained using `Box::into_raw`, and will
/// free the data automatically.
pub unsafe fn set_plugin_data(&mut self, data: *mut ()) {
sys::zathura_document_set_data(self.ptr, data as *mut _)
}
}