pdfium_render/pdf/path/
segment.rs

1//! Defines the [PdfPathSegment] struct, exposing functionality related to a single
2//! path segment in a `PdfPathSegments` collection.
3
4use crate::bindgen::{
5    FPDF_PATHSEGMENT, FPDF_SEGMENT_BEZIERTO, FPDF_SEGMENT_LINETO, FPDF_SEGMENT_MOVETO,
6    FPDF_SEGMENT_UNKNOWN,
7};
8use crate::bindings::PdfiumLibraryBindings;
9use crate::error::PdfiumError;
10use crate::pdf::matrix::PdfMatrix;
11use crate::pdf::points::PdfPoints;
12use std::os::raw::c_float;
13
14/// The type of a single [PdfPathSegment].
15#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
16pub enum PdfPathSegmentType {
17    Unknown = FPDF_SEGMENT_UNKNOWN as isize,
18    LineTo = FPDF_SEGMENT_LINETO as isize,
19    BezierTo = FPDF_SEGMENT_BEZIERTO as isize,
20    MoveTo = FPDF_SEGMENT_MOVETO as isize,
21}
22
23impl PdfPathSegmentType {
24    #[inline]
25    pub(crate) fn from_pdfium(segment_type: i32) -> Result<PdfPathSegmentType, PdfiumError> {
26        if segment_type == FPDF_SEGMENT_UNKNOWN {
27            return Ok(PdfPathSegmentType::Unknown);
28        }
29
30        match segment_type as u32 {
31            FPDF_SEGMENT_LINETO => Ok(PdfPathSegmentType::LineTo),
32            FPDF_SEGMENT_BEZIERTO => Ok(PdfPathSegmentType::BezierTo),
33            FPDF_SEGMENT_MOVETO => Ok(PdfPathSegmentType::MoveTo),
34            _ => Err(PdfiumError::UnknownPathSegmentType),
35        }
36    }
37}
38
39/// A single [PdfPathSegment] in a `PdfPathSegments` collection.
40pub struct PdfPathSegment<'a> {
41    handle: FPDF_PATHSEGMENT,
42    matrix: Option<PdfMatrix>,
43    bindings: &'a dyn PdfiumLibraryBindings,
44}
45
46impl<'a> PdfPathSegment<'a> {
47    #[inline]
48    pub(crate) fn from_pdfium(
49        handle: FPDF_PATHSEGMENT,
50        matrix: Option<PdfMatrix>,
51        bindings: &'a dyn PdfiumLibraryBindings,
52    ) -> Self {
53        Self {
54            handle,
55            matrix,
56            bindings,
57        }
58    }
59
60    /// Returns the [PdfiumLibraryBindings] used by this [PdfPathSegment].
61    #[inline]
62    pub fn bindings(&self) -> &'a dyn PdfiumLibraryBindings {
63        self.bindings
64    }
65
66    /// Returns the [PdfPathSegmentType] of this [PdfPathSegment].
67    #[inline]
68    pub fn segment_type(&self) -> PdfPathSegmentType {
69        PdfPathSegmentType::from_pdfium(self.bindings().FPDFPathSegment_GetType(self.handle))
70            .unwrap_or(PdfPathSegmentType::Unknown)
71    }
72
73    /// Returns `true` if this [PdfPathSegment] closes the current sub-path.
74    #[inline]
75    pub fn is_close(&self) -> bool {
76        self.bindings()
77            .is_true(self.bindings().FPDFPathSegment_GetClose(self.handle))
78    }
79
80    /// Returns the horizontal and vertical destination positions of this [PdfPathSegment].
81    pub fn point(&self) -> (PdfPoints, PdfPoints) {
82        let mut x: c_float = 0.0;
83
84        let mut y: c_float = 0.0;
85
86        if self
87            .bindings()
88            .is_true(
89                self.bindings()
90                    .FPDFPathSegment_GetPoint(self.handle, &mut x, &mut y),
91            )
92        {
93            let x = PdfPoints::new(x as f32);
94
95            let y = PdfPoints::new(y as f32);
96
97            match self.matrix.as_ref() {
98                None => (x, y),
99                Some(matrix) => matrix.apply_to_points(x, y),
100            }
101        } else {
102            (PdfPoints::ZERO, PdfPoints::ZERO)
103        }
104    }
105
106    /// Returns the horizontal destination position of this [PdfPathSegment].
107    #[inline]
108    pub fn x(&self) -> PdfPoints {
109        self.point().0
110    }
111
112    /// Returns the vertical destination position of this [PdfPathSegment].
113    #[inline]
114    pub fn y(&self) -> PdfPoints {
115        self.point().1
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use crate::prelude::*;
122    use crate::utils::test::test_bind_to_pdfium;
123
124    #[test]
125    fn test_point_transform() {
126        let pdfium = test_bind_to_pdfium();
127
128        let mut document = pdfium.create_new_pdf().unwrap();
129
130        let mut page = document
131            .pages_mut()
132            .create_page_at_start(PdfPagePaperSize::a4())
133            .unwrap();
134
135        let object = page
136            .objects_mut()
137            .create_path_object_line(
138                PdfPoints::new(100.0),
139                PdfPoints::new(200.0),
140                PdfPoints::new(300.0),
141                PdfPoints::new(400.0),
142                PdfColor::BEIGE,
143                PdfPoints::new(1.0),
144            )
145            .unwrap();
146
147        let delta_x = PdfPoints::new(50.0);
148        let delta_y = PdfPoints::new(-25.0);
149
150        let matrix = PdfMatrix::identity().translate(delta_x, delta_y).unwrap();
151
152        let raw_segment_0 = object.as_path_object().unwrap().segments().get(0).unwrap();
153        let raw_segment_1 = object.as_path_object().unwrap().segments().get(1).unwrap();
154
155        let transformed_segment_0 = object
156            .as_path_object()
157            .unwrap()
158            .segments()
159            .transform(matrix)
160            .get(0)
161            .unwrap();
162
163        let transformed_segment_1 = object
164            .as_path_object()
165            .unwrap()
166            .segments()
167            .transform(matrix)
168            .get(1)
169            .unwrap();
170
171        assert_eq!(transformed_segment_0.x(), raw_segment_0.x() + delta_x);
172        assert_eq!(transformed_segment_0.y(), raw_segment_0.y() + delta_y);
173        assert_eq!(transformed_segment_1.x(), raw_segment_1.x() + delta_x);
174        assert_eq!(transformed_segment_1.y(), raw_segment_1.y() + delta_y);
175    }
176
177    #[test]
178    fn test_point_transform_during_iteration() {
179        let pdfium = test_bind_to_pdfium();
180
181        let mut document = pdfium.create_new_pdf().unwrap();
182
183        let mut page = document
184            .pages_mut()
185            .create_page_at_start(PdfPagePaperSize::a4())
186            .unwrap();
187
188        let object = page
189            .objects_mut()
190            .create_path_object_line(
191                PdfPoints::new(100.0),
192                PdfPoints::new(200.0),
193                PdfPoints::new(300.0),
194                PdfPoints::new(400.0),
195                PdfColor::BEIGE,
196                PdfPoints::new(1.0),
197            )
198            .unwrap();
199
200        let raw_points: Vec<(PdfPoints, PdfPoints)> = object
201            .as_path_object()
202            .unwrap()
203            .segments()
204            .iter()
205            .map(|segment| segment.point())
206            .collect();
207
208        let delta_x = PdfPoints::new(50.0);
209        let delta_y = PdfPoints::new(-25.0);
210
211        let matrix = PdfMatrix::identity().translate(delta_x, delta_y).unwrap();
212
213        let transformed_points: Vec<(PdfPoints, PdfPoints)> = object
214            .as_path_object()
215            .unwrap()
216            .segments()
217            .transform(matrix)
218            .iter()
219            .map(|segment| segment.point())
220            .collect();
221
222        for (raw, transformed) in raw_points.iter().zip(transformed_points) {
223            assert_eq!(transformed.0, raw.0 + delta_x);
224            assert_eq!(transformed.1, raw.1 + delta_y);
225        }
226    }
227}