pdfium_render/pdf/path/
clip_path.rs

1//! Defines the [PdfClipPath] struct, exposing functionality related to a clip path.
2
3use crate::bindgen::FPDF_CLIPPATH;
4use crate::bindings::PdfiumLibraryBindings;
5use crate::error::{PdfiumError, PdfiumInternalError};
6use crate::pdf::document::page::object::ownership::PdfPageObjectOwnership;
7use crate::pdf::path::segment::PdfPathSegment;
8use crate::pdf::path::segments::{PdfPathSegmentIndex, PdfPathSegments, PdfPathSegmentsIterator};
9use std::convert::TryInto;
10use std::ops::{Range, RangeInclusive};
11use std::os::raw::c_int;
12
13/// The zero-based index of a single [PdfClipPathSegments] path object inside its
14/// containing [PdfClipPath] instance.
15pub type PdfClipPathSegmentIndex = u16;
16
17/// A single clip path, containing zero or more path objects.
18pub struct PdfClipPath<'a> {
19    handle: FPDF_CLIPPATH,
20    ownership: PdfPageObjectOwnership,
21    bindings: &'a dyn PdfiumLibraryBindings,
22}
23
24impl<'a> PdfClipPath<'a> {
25    #[inline]
26    pub(crate) fn from_pdfium(
27        handle: FPDF_CLIPPATH,
28        ownership: PdfPageObjectOwnership,
29        bindings: &'a dyn PdfiumLibraryBindings,
30    ) -> Self {
31        Self {
32            handle,
33            ownership,
34            bindings,
35        }
36    }
37
38    /// Returns the internal `FPDF_CLIPPATH` handle for this [PdfPathSegment].
39    #[inline]
40    pub(crate) fn handle(&self) -> FPDF_CLIPPATH {
41        self.handle
42    }
43
44    /// Returns the [PdfiumLibraryBindings] used by this [PdfClipPath] instance.
45    #[inline]
46    pub fn bindings(&self) -> &'a dyn PdfiumLibraryBindings {
47        self.bindings
48    }
49
50    /// Returns the number of path objects inside this [PdfClipPath] instance.
51    #[inline]
52    pub fn len(&self) -> PdfClipPathSegmentIndex {
53        self.bindings().FPDFClipPath_CountPaths(self.handle()) as PdfClipPathSegmentIndex
54    }
55
56    /// Returns `true` if this [PdfClipPath] instance is empty.
57    #[inline]
58    pub fn is_empty(&self) -> bool {
59        self.len() == 0
60    }
61
62    /// Returns a Range from `0..(number of path objects)` for this [PdfClipPath] instance.
63    #[inline]
64    pub fn as_range(&self) -> Range<PdfClipPathSegmentIndex> {
65        0..self.len()
66    }
67
68    /// Returns an inclusive Range from `0..=(number of path objects - 1)` for this [PdfClipPath] instance.
69    #[inline]
70    pub fn as_range_inclusive(&self) -> RangeInclusive<PdfClipPathSegmentIndex> {
71        if self.is_empty() {
72            0..=0
73        } else {
74            0..=(self.len() - 1)
75        }
76    }
77
78    /// Returns a single [PdfClipPathSegments] path object from this [PdfClipPath] instance.
79    pub fn get(
80        &self,
81        index: PdfClipPathSegmentIndex,
82    ) -> Result<PdfClipPathSegments<'a>, PdfiumError> {
83        if index >= self.len() {
84            return Err(PdfiumError::PdfClipPathSegmentIndexOutOfBounds);
85        }
86
87        Ok(PdfClipPathSegments::from_pdfium(
88            self.handle(),
89            index,
90            self.bindings(),
91        ))
92    }
93
94    /// Returns an iterator over all the path objects in this [PdfClipPath] instance.
95    #[inline]
96    pub fn iter(&self) -> PdfClipPathIterator<'_> {
97        PdfClipPathIterator::new(self)
98    }
99}
100
101impl<'a> Drop for PdfClipPath<'a> {
102    /// Closes this [PdfClipPath], releasing held memory.
103    #[inline]
104    fn drop(&mut self) {
105        if !self.ownership.is_owned() {
106            // Responsibility for de-allocation lies with us, not Pdfium, since
107            // the clip path is not attached to a page, a page object, or an annotation.
108
109            self.bindings.FPDF_DestroyClipPath(self.handle)
110        }
111    }
112}
113
114/// An iterator over all the [PdfPathSegments] path objects in a [PdfClipPath] instance.
115pub struct PdfClipPathIterator<'a> {
116    clip_path: &'a PdfClipPath<'a>,
117    next_index: PdfClipPathSegmentIndex,
118}
119
120impl<'a> PdfClipPathIterator<'a> {
121    #[inline]
122    pub(crate) fn new(clip_path: &'a PdfClipPath<'a>) -> Self {
123        PdfClipPathIterator {
124            clip_path,
125            next_index: 0,
126        }
127    }
128}
129
130impl<'a> Iterator for PdfClipPathIterator<'a> {
131    type Item = PdfClipPathSegments<'a>;
132
133    fn next(&mut self) -> Option<Self::Item> {
134        let next = self.clip_path.get(self.next_index);
135
136        self.next_index += 1;
137
138        next.ok()
139    }
140}
141
142/// The collection of [PdfPathSegment] objects inside a single path within a clip path.
143pub struct PdfClipPathSegments<'a> {
144    handle: FPDF_CLIPPATH,
145    index: PdfClipPathSegmentIndex,
146    bindings: &'a dyn PdfiumLibraryBindings,
147}
148
149impl<'a> PdfClipPathSegments<'a> {
150    #[inline]
151    pub(crate) fn from_pdfium(
152        handle: FPDF_CLIPPATH,
153        path_index: PdfClipPathSegmentIndex,
154        bindings: &'a dyn PdfiumLibraryBindings,
155    ) -> Self {
156        Self {
157            handle,
158            index: path_index,
159            bindings,
160        }
161    }
162}
163
164impl<'a> PdfPathSegments<'a> for PdfClipPathSegments<'a> {
165    #[inline]
166    fn bindings(&self) -> &'a dyn PdfiumLibraryBindings {
167        self.bindings
168    }
169
170    #[inline]
171    fn len(&self) -> PdfPathSegmentIndex {
172        self.bindings()
173            .FPDFClipPath_CountPathSegments(self.handle, self.index as i32)
174            .try_into()
175            .unwrap_or(0)
176    }
177
178    fn get(&self, index: PdfPathSegmentIndex) -> Result<PdfPathSegment<'a>, PdfiumError> {
179        let handle = self.bindings().FPDFClipPath_GetPathSegment(
180            self.handle,
181            self.index as i32,
182            index as c_int,
183        );
184
185        if handle.is_null() {
186            Err(PdfiumError::PdfiumLibraryInternalError(
187                PdfiumInternalError::Unknown,
188            ))
189        } else {
190            Ok(PdfPathSegment::from_pdfium(handle, None, self.bindings()))
191        }
192    }
193
194    #[inline]
195    fn iter(&'a self) -> PdfPathSegmentsIterator<'a> {
196        PdfPathSegmentsIterator::new(self)
197    }
198}