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