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::bindings::PdfiumLibraryBindings;
9use crate::error::PdfiumError;
10use crate::pdf::matrix::PdfMatrix;
11use crate::pdf::points::PdfPoints;
12use std::os::raw::c_float;
13
14#[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
39pub 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 #[inline]
62 pub(crate) fn handle(&self) -> FPDF_PATHSEGMENT {
63 self.handle
64 }
65
66 #[inline]
68 pub fn bindings(&self) -> &'a dyn PdfiumLibraryBindings {
69 self.bindings
70 }
71
72 #[inline]
74 pub fn segment_type(&self) -> PdfPathSegmentType {
75 PdfPathSegmentType::from_pdfium(self.bindings().FPDFPathSegment_GetType(self.handle))
76 .unwrap_or(PdfPathSegmentType::Unknown)
77 }
78
79 #[inline]
81 pub fn is_close(&self) -> bool {
82 self.bindings()
83 .is_true(self.bindings().FPDFPathSegment_GetClose(self.handle()))
84 }
85
86 pub fn point(&self) -> (PdfPoints, PdfPoints) {
88 let mut x: c_float = 0.0;
89
90 let mut y: c_float = 0.0;
91
92 if self
93 .bindings()
94 .is_true(
95 self.bindings()
96 .FPDFPathSegment_GetPoint(self.handle(), &mut x, &mut y),
97 )
98 {
99 let x = PdfPoints::new(x as f32);
100
101 let y = PdfPoints::new(y as f32);
102
103 match self.matrix.as_ref() {
104 None => (x, y),
105 Some(matrix) => matrix.apply_to_points(x, y),
106 }
107 } else {
108 (PdfPoints::ZERO, PdfPoints::ZERO)
109 }
110 }
111
112 #[inline]
114 pub fn x(&self) -> PdfPoints {
115 self.point().0
116 }
117
118 #[inline]
120 pub fn y(&self) -> PdfPoints {
121 self.point().1
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use crate::prelude::*;
128 use crate::utils::test::test_bind_to_pdfium;
129
130 #[test]
131 fn test_point_transform() {
132 let pdfium = test_bind_to_pdfium();
133
134 let mut document = pdfium.create_new_pdf().unwrap();
135
136 let mut page = document
137 .pages_mut()
138 .create_page_at_start(PdfPagePaperSize::a4())
139 .unwrap();
140
141 let object = page
142 .objects_mut()
143 .create_path_object_line(
144 PdfPoints::new(100.0),
145 PdfPoints::new(200.0),
146 PdfPoints::new(300.0),
147 PdfPoints::new(400.0),
148 PdfColor::BEIGE,
149 PdfPoints::new(1.0),
150 )
151 .unwrap();
152
153 let delta_x = PdfPoints::new(50.0);
154 let delta_y = PdfPoints::new(-25.0);
155
156 let matrix = PdfMatrix::identity().translate(delta_x, delta_y).unwrap();
157
158 let raw_segment_0 = object.as_path_object().unwrap().segments().get(0).unwrap();
159 let raw_segment_1 = object.as_path_object().unwrap().segments().get(1).unwrap();
160
161 let transformed_segment_0 = object
162 .as_path_object()
163 .unwrap()
164 .segments()
165 .transform(matrix)
166 .get(0)
167 .unwrap();
168
169 let transformed_segment_1 = object
170 .as_path_object()
171 .unwrap()
172 .segments()
173 .transform(matrix)
174 .get(1)
175 .unwrap();
176
177 assert_eq!(transformed_segment_0.x(), raw_segment_0.x() + delta_x);
178 assert_eq!(transformed_segment_0.y(), raw_segment_0.y() + delta_y);
179 assert_eq!(transformed_segment_1.x(), raw_segment_1.x() + delta_x);
180 assert_eq!(transformed_segment_1.y(), raw_segment_1.y() + delta_y);
181 }
182
183 #[test]
184 fn test_point_transform_during_iteration() {
185 let pdfium = test_bind_to_pdfium();
186
187 let mut document = pdfium.create_new_pdf().unwrap();
188
189 let mut page = document
190 .pages_mut()
191 .create_page_at_start(PdfPagePaperSize::a4())
192 .unwrap();
193
194 let object = page
195 .objects_mut()
196 .create_path_object_line(
197 PdfPoints::new(100.0),
198 PdfPoints::new(200.0),
199 PdfPoints::new(300.0),
200 PdfPoints::new(400.0),
201 PdfColor::BEIGE,
202 PdfPoints::new(1.0),
203 )
204 .unwrap();
205
206 let raw_points: Vec<(PdfPoints, PdfPoints)> = object
207 .as_path_object()
208 .unwrap()
209 .segments()
210 .iter()
211 .map(|segment| segment.point())
212 .collect();
213
214 let delta_x = PdfPoints::new(50.0);
215 let delta_y = PdfPoints::new(-25.0);
216
217 let matrix = PdfMatrix::identity().translate(delta_x, delta_y).unwrap();
218
219 let transformed_points: Vec<(PdfPoints, PdfPoints)> = object
220 .as_path_object()
221 .unwrap()
222 .segments()
223 .transform(matrix)
224 .iter()
225 .map(|segment| segment.point())
226 .collect();
227
228 for (raw, transformed) in raw_points.iter().zip(transformed_points) {
229 assert_eq!(transformed.0, raw.0 + delta_x);
230 assert_eq!(transformed.1, raw.1 + delta_y);
231 }
232 }
233}