1use std::{fmt, os::fd::AsFd, str::FromStr};
35
36use serde::{Deserialize, Serialize};
37use zbus::zvariant::{DeserializeDict, Fd, SerializeDict, Type};
38
39use super::{HandleToken, Request};
40use crate::{proxy::Proxy, Error, WindowIdentifier};
41
42#[cfg_attr(feature = "glib", derive(glib::Enum))]
43#[cfg_attr(feature = "glib", enum_type(name = "AshpdOrientation"))]
44#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq, Eq, Type)]
45#[zvariant(signature = "s")]
46#[serde(rename_all = "snake_case")]
47pub enum Orientation {
49 Landscape,
51 Portrait,
53 ReverseLandscape,
55 ReversePortrait,
57}
58
59impl fmt::Display for Orientation {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 match self {
62 Self::Landscape => write!(f, "Landscape"),
63 Self::Portrait => write!(f, "Portrait"),
64 Self::ReverseLandscape => write!(f, "Reverse Landscape"),
65 Self::ReversePortrait => write!(f, "Reverse Portrait"),
66 }
67 }
68}
69
70impl AsRef<str> for Orientation {
71 fn as_ref(&self) -> &str {
72 match self {
73 Self::Landscape => "Landscape",
74 Self::Portrait => "Portrait",
75 Self::ReverseLandscape => "Reverse Landscape",
76 Self::ReversePortrait => "Reverse Portrait",
77 }
78 }
79}
80
81impl From<Orientation> for &'static str {
82 fn from(o: Orientation) -> Self {
83 match o {
84 Orientation::Landscape => "Landscape",
85 Orientation::Portrait => "Portrait",
86 Orientation::ReverseLandscape => "Reverse Landscape",
87 Orientation::ReversePortrait => "Reverse Portrait",
88 }
89 }
90}
91
92impl FromStr for Orientation {
93 type Err = Error;
94
95 fn from_str(s: &str) -> Result<Self, Self::Err> {
96 match s {
97 "Landscape" | "landscape" => Ok(Orientation::Landscape),
98 "Portrait" | "portrait" => Ok(Orientation::Portrait),
99 "ReverseLandscape" | "reverse_landscape" => Ok(Orientation::ReverseLandscape),
100 "ReversePortrait" | "reverse_portrait" => Ok(Orientation::ReversePortrait),
101 _ => Err(Error::ParseError(
102 "Failed to parse orientation, invalid value",
103 )),
104 }
105 }
106}
107
108#[cfg_attr(feature = "glib", derive(glib::Enum))]
109#[cfg_attr(feature = "glib", enum_type(name = "AshpdQuality"))]
110#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Type)]
111#[zvariant(signature = "s")]
112#[serde(rename_all = "lowercase")]
113pub enum Quality {
115 Draft,
117 Low,
119 Normal,
121 High,
123}
124
125impl fmt::Display for Quality {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 match self {
128 Self::Draft => write!(f, "Draft"),
129 Self::Low => write!(f, "Low"),
130 Self::Normal => write!(f, "Normal"),
131 Self::High => write!(f, "High"),
132 }
133 }
134}
135
136impl AsRef<str> for Quality {
137 fn as_ref(&self) -> &str {
138 match self {
139 Self::Draft => "Draft",
140 Self::Low => "Low",
141 Self::Normal => "Normal",
142 Self::High => "High",
143 }
144 }
145}
146
147impl From<Quality> for &'static str {
148 fn from(q: Quality) -> Self {
149 match q {
150 Quality::Draft => "Draft",
151 Quality::Low => "Low",
152 Quality::Normal => "Normal",
153 Quality::High => "High",
154 }
155 }
156}
157
158impl FromStr for Quality {
159 type Err = Error;
160
161 fn from_str(s: &str) -> Result<Self, Self::Err> {
162 match s {
163 "Draft" | "draft" => Ok(Quality::Draft),
164 "Low" | "low" => Ok(Quality::Low),
165 "Normal" | "normal" => Ok(Quality::Normal),
166 "High" | "high" => Ok(Quality::High),
167 _ => Err(Error::ParseError("Failed to parse quality, invalid value")),
168 }
169 }
170}
171
172#[derive(SerializeDict, DeserializeDict, Type, Debug, Default)]
173#[zvariant(signature = "dict")]
175pub struct Settings {
176 pub orientation: Option<Orientation>,
178 #[zvariant(rename = "paper-format")]
180 pub paper_format: Option<String>,
181 #[zvariant(rename = "paper-width")]
183 pub paper_width: Option<String>,
184 #[zvariant(rename = "paper-height")]
186 pub paper_height: Option<String>,
187 #[zvariant(rename = "n-copies")]
189 pub n_copies: Option<String>,
190 #[zvariant(rename = "default-source")]
192 pub default_source: Option<String>,
193 pub quality: Option<Quality>,
195 pub resolution: Option<String>,
197 #[zvariant(rename = "use-color")]
199 pub use_color: Option<String>,
200 pub duplex: Option<String>,
202 pub collate: Option<String>,
204 pub reverse: Option<String>,
206 #[zvariant(rename = "media-type")]
208 pub media_type: Option<String>,
209 pub dither: Option<String>,
212 pub scale: Option<String>,
214 #[zvariant(rename = "print-pages")]
216 pub print_pages: Option<String>,
217 #[zvariant(rename = "page-ranges")]
219 pub page_ranges: Option<String>,
220 #[zvariant(rename = "page-set")]
222 pub page_set: Option<String>,
223 pub finishings: Option<String>,
225 #[zvariant(rename = "number-up")]
227 pub number_up: Option<String>,
228 #[zvariant(rename = "number-up-layout")]
230 pub number_up_layout: Option<String>,
231 #[zvariant(rename = "output-bin")]
232 pub output_bin: Option<String>,
234 #[zvariant(rename = "resolution-x")]
236 pub resolution_x: Option<String>,
237 #[zvariant(rename = "resolution-y")]
239 pub resolution_y: Option<String>,
240 #[zvariant(rename = "printer-lpi")]
242 pub print_lpi: Option<String>,
243 #[zvariant(rename = "output-basename")]
245 pub output_basename: Option<String>,
246 #[zvariant(rename = "output-file-format")]
248 pub output_file_format: Option<String>,
249 #[zvariant(rename = "output-uri")]
251 pub output_uri: Option<url::Url>,
252}
253
254impl Settings {
255 #[must_use]
257 pub fn orientation(mut self, orientation: impl Into<Option<Orientation>>) -> Self {
258 self.orientation = orientation.into();
259 self
260 }
261
262 #[must_use]
264 pub fn paper_format<'a>(mut self, paper_format: impl Into<Option<&'a str>>) -> Self {
265 self.paper_format = paper_format.into().map(ToOwned::to_owned);
266 self
267 }
268
269 #[must_use]
271 pub fn paper_width<'a>(mut self, paper_width: impl Into<Option<&'a str>>) -> Self {
272 self.paper_width = paper_width.into().map(ToOwned::to_owned);
273 self
274 }
275
276 #[must_use]
278 pub fn paper_height<'a>(mut self, paper_height: impl Into<Option<&'a str>>) -> Self {
279 self.paper_height = paper_height.into().map(ToOwned::to_owned);
280 self
281 }
282
283 #[must_use]
285 pub fn n_copies<'a>(mut self, n_copies: impl Into<Option<&'a str>>) -> Self {
286 self.n_copies = n_copies.into().map(ToOwned::to_owned);
287 self
288 }
289
290 #[must_use]
292 pub fn default_source<'a>(mut self, default_source: impl Into<Option<&'a str>>) -> Self {
293 self.default_source = default_source.into().map(ToOwned::to_owned);
294 self
295 }
296
297 #[must_use]
299 pub fn quality(mut self, quality: impl Into<Option<Quality>>) -> Self {
300 self.quality = quality.into();
301 self
302 }
303
304 #[must_use]
306 pub fn resolution<'a>(mut self, resolution: impl Into<Option<&'a str>>) -> Self {
307 self.resolution = resolution.into().map(ToOwned::to_owned);
308 self
309 }
310
311 #[must_use]
313 pub fn use_color(mut self, use_color: impl Into<Option<bool>>) -> Self {
314 let use_color = use_color.into().unwrap_or_default();
315 if use_color {
316 self.use_color = Some("yes".to_owned());
317 } else {
318 self.use_color = Some("no".to_owned())
319 };
320 self
321 }
322
323 #[must_use]
325 pub fn duplex<'a>(mut self, duplex: impl Into<Option<&'a str>>) -> Self {
326 self.duplex = duplex.into().map(ToOwned::to_owned);
327 self
328 }
329
330 #[must_use]
332 pub fn collate(mut self, collate: impl Into<Option<bool>>) -> Self {
333 let collate = collate.into().unwrap_or_default();
334 if collate {
335 self.collate = Some("yes".to_owned());
336 } else {
337 self.collate = Some("no".to_owned())
338 };
339 self
340 }
341
342 #[must_use]
344 pub fn reverse(mut self, reverse: impl Into<Option<bool>>) -> Self {
345 let reverse = reverse.into().unwrap_or_default();
346 if reverse {
347 self.reverse = Some("yes".to_owned());
348 } else {
349 self.reverse = Some("no".to_owned())
350 };
351 self
352 }
353
354 #[must_use]
356 pub fn media_type<'a>(mut self, media_type: impl Into<Option<&'a str>>) -> Self {
357 self.media_type = media_type.into().map(ToOwned::to_owned);
358 self
359 }
360
361 #[must_use]
363 pub fn dither<'a>(mut self, dither: impl Into<Option<&'a str>>) -> Self {
364 self.dither = dither.into().map(ToOwned::to_owned);
365 self
366 }
367
368 #[must_use]
370 pub fn scale<'a>(mut self, scale: impl Into<Option<&'a str>>) -> Self {
371 self.scale = scale.into().map(ToOwned::to_owned);
372 self
373 }
374
375 #[must_use]
377 pub fn print_pages<'a>(mut self, print_pages: impl Into<Option<&'a str>>) -> Self {
378 self.print_pages = print_pages.into().map(ToOwned::to_owned);
379 self
380 }
381
382 #[must_use]
384 pub fn page_ranges<'a>(mut self, page_ranges: impl Into<Option<&'a str>>) -> Self {
385 self.page_ranges = page_ranges.into().map(ToOwned::to_owned);
386 self
387 }
388
389 #[must_use]
391 pub fn page_set<'a>(mut self, page_set: impl Into<Option<&'a str>>) -> Self {
392 self.page_set = page_set.into().map(ToOwned::to_owned);
393 self
394 }
395
396 #[must_use]
398 pub fn finishings<'a>(mut self, finishings: impl Into<Option<&'a str>>) -> Self {
399 self.finishings = finishings.into().map(ToOwned::to_owned);
400 self
401 }
402
403 #[must_use]
405 pub fn number_up<'a>(mut self, number_up: impl Into<Option<&'a str>>) -> Self {
406 self.number_up = number_up.into().map(ToOwned::to_owned);
407 self
408 }
409
410 #[must_use]
413 pub fn number_up_layout<'a>(mut self, number_up_layout: impl Into<Option<&'a str>>) -> Self {
414 self.number_up_layout = number_up_layout.into().map(ToOwned::to_owned);
415 self
416 }
417
418 #[must_use]
420 pub fn output_bin<'a>(mut self, output_bin: impl Into<Option<&'a str>>) -> Self {
421 self.output_bin = output_bin.into().map(ToOwned::to_owned);
422 self
423 }
424
425 #[must_use]
427 pub fn resolution_x<'a>(mut self, resolution_x: impl Into<Option<&'a str>>) -> Self {
428 self.resolution_x = resolution_x.into().map(ToOwned::to_owned);
429 self
430 }
431
432 #[must_use]
434 pub fn resolution_y<'a>(mut self, resolution_y: impl Into<Option<&'a str>>) -> Self {
435 self.resolution_y = resolution_y.into().map(ToOwned::to_owned);
436 self
437 }
438
439 #[must_use]
441 pub fn print_lpi<'a>(mut self, print_lpi: impl Into<Option<&'a str>>) -> Self {
442 self.print_lpi = print_lpi.into().map(ToOwned::to_owned);
443 self
444 }
445
446 #[must_use]
448 pub fn output_basename<'a>(mut self, output_basename: impl Into<Option<&'a str>>) -> Self {
449 self.output_basename = output_basename.into().map(ToOwned::to_owned);
450 self
451 }
452
453 #[must_use]
455 pub fn output_file_format<'a>(
456 mut self,
457 output_file_format: impl Into<Option<&'a str>>,
458 ) -> Self {
459 self.output_file_format = output_file_format.into().map(ToOwned::to_owned);
460 self
461 }
462
463 #[must_use]
465 pub fn output_uri<'a>(mut self, output_uri: impl Into<Option<&'a url::Url>>) -> Self {
466 self.output_uri = output_uri.into().map(ToOwned::to_owned);
467 self
468 }
469}
470
471#[derive(SerializeDict, DeserializeDict, Type, Debug, Default)]
472#[zvariant(signature = "dict")]
474pub struct PageSetup {
475 #[zvariant(rename = "PPDName")]
477 pub ppdname: Option<String>,
478 #[zvariant(rename = "Name")]
480 pub name: Option<String>,
481 #[zvariant(rename = "DisplayName")]
483 pub display_name: Option<String>,
484 #[zvariant(rename = "Width")]
486 pub width: Option<f64>,
487 #[zvariant(rename = "Height")]
489 pub height: Option<f64>,
490 #[zvariant(rename = "MarginTop")]
492 pub margin_top: Option<f64>,
493 #[zvariant(rename = "MarginBottom")]
495 pub margin_bottom: Option<f64>,
496 #[zvariant(rename = "MarginRight")]
498 pub margin_right: Option<f64>,
499 #[zvariant(rename = "MarginLeft")]
501 pub margin_left: Option<f64>,
502 #[zvariant(rename = "Orientation")]
504 pub orientation: Option<Orientation>,
505}
506
507impl PageSetup {
508 #[must_use]
510 pub fn ppdname<'a>(mut self, ppdname: impl Into<Option<&'a str>>) -> Self {
511 self.ppdname = ppdname.into().map(ToOwned::to_owned);
512 self
513 }
514
515 #[must_use]
517 pub fn name<'a>(mut self, name: impl Into<Option<&'a str>>) -> Self {
518 self.name = name.into().map(ToOwned::to_owned);
519 self
520 }
521
522 #[must_use]
524 pub fn display_name<'a>(mut self, display_name: impl Into<Option<&'a str>>) -> Self {
525 self.display_name = display_name.into().map(ToOwned::to_owned);
526 self
527 }
528
529 #[must_use]
531 pub fn orientation(mut self, orientation: impl Into<Option<Orientation>>) -> Self {
532 self.orientation = orientation.into();
533 self
534 }
535
536 #[must_use]
538 pub fn width(mut self, width: impl Into<Option<f64>>) -> Self {
539 self.width = width.into();
540 self
541 }
542
543 #[must_use]
545 pub fn height(mut self, height: impl Into<Option<f64>>) -> Self {
546 self.height = height.into();
547 self
548 }
549
550 #[must_use]
552 pub fn margin_top(mut self, margin_top: impl Into<Option<f64>>) -> Self {
553 self.margin_top = margin_top.into();
554 self
555 }
556
557 #[must_use]
559 pub fn margin_bottom(mut self, margin_bottom: impl Into<Option<f64>>) -> Self {
560 self.margin_bottom = margin_bottom.into();
561 self
562 }
563
564 #[must_use]
566 pub fn margin_right(mut self, margin_right: impl Into<Option<f64>>) -> Self {
567 self.margin_right = margin_right.into();
568 self
569 }
570
571 #[must_use]
573 pub fn margin_left(mut self, margin_left: impl Into<Option<f64>>) -> Self {
574 self.margin_left = margin_left.into();
575 self
576 }
577}
578
579#[derive(SerializeDict, Type, Debug, Default)]
580#[zvariant(signature = "dict")]
582struct PreparePrintOptions {
583 handle_token: HandleToken,
585 modal: Option<bool>,
587 accept_label: Option<String>,
589}
590
591impl PreparePrintOptions {
592 #[must_use]
594 pub fn modal(mut self, modal: impl Into<Option<bool>>) -> Self {
595 self.modal = modal.into();
596 self
597 }
598
599 #[must_use]
601 pub fn accept_label<'a>(mut self, accept_label: impl Into<Option<&'a str>>) -> Self {
602 self.accept_label = accept_label.into().map(ToOwned::to_owned);
603 self
604 }
605}
606
607#[derive(SerializeDict, Type, Debug, Default)]
608#[zvariant(signature = "dict")]
610struct PrintOptions {
611 handle_token: HandleToken,
613 modal: Option<bool>,
615 token: Option<u32>,
618}
619
620impl PrintOptions {
621 pub fn token(mut self, token: impl Into<Option<u32>>) -> Self {
623 self.token = token.into();
624 self
625 }
626
627 pub fn modal(mut self, modal: impl Into<Option<bool>>) -> Self {
629 self.modal = modal.into();
630 self
631 }
632}
633
634#[derive(DeserializeDict, SerializeDict, Type, Debug)]
635#[zvariant(signature = "dict")]
637pub struct PreparePrint {
638 pub settings: Settings,
640 #[zvariant(rename = "page-setup")]
641 pub page_setup: PageSetup,
643 pub token: u32,
645}
646
647#[derive(Debug)]
651#[doc(alias = "org.freedesktop.portal.Print")]
652pub struct PrintProxy<'a>(Proxy<'a>);
653
654impl<'a> PrintProxy<'a> {
655 pub async fn new() -> Result<PrintProxy<'a>, Error> {
657 let proxy = Proxy::new_desktop("org.freedesktop.portal.Print").await?;
658 Ok(Self(proxy))
659 }
660
661 #[doc(alias = "PreparePrint")]
679 #[doc(alias = "xdp_portal_prepare_print")]
680 pub async fn prepare_print(
681 &self,
682 identifier: Option<&WindowIdentifier>,
683 title: &str,
684 settings: Settings,
685 page_setup: PageSetup,
686 accept_label: impl Into<Option<&'a str>>,
687 modal: bool,
688 ) -> Result<Request<PreparePrint>, Error> {
689 let options = PreparePrintOptions::default()
690 .modal(modal)
691 .accept_label(accept_label);
692 let identifier = identifier.map(|i| i.to_string()).unwrap_or_default();
693 self.0
694 .request(
695 &options.handle_token,
696 "PreparePrint",
697 &(&identifier, title, settings, page_setup, &options),
698 )
699 .await
700 }
701
702 #[doc(alias = "Print")]
720 #[doc(alias = "xdp_portal_print_file")]
721 pub async fn print(
722 &self,
723 identifier: Option<&WindowIdentifier>,
724 title: &str,
725 fd: &impl AsFd,
726 token: Option<u32>,
727 modal: bool,
728 ) -> Result<Request<()>, Error> {
729 let options = PrintOptions::default()
730 .token(token.unwrap_or(0))
731 .modal(modal);
732 let identifier = identifier.map(|i| i.to_string()).unwrap_or_default();
733
734 self.0
735 .empty_request(
736 &options.handle_token,
737 "Print",
738 &(&identifier, title, Fd::from(fd), &options),
739 )
740 .await
741 }
742}
743
744impl<'a> std::ops::Deref for PrintProxy<'a> {
745 type Target = zbus::Proxy<'a>;
746
747 fn deref(&self) -> &Self::Target {
748 &self.0
749 }
750}