Skip to main content

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