• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "path_object.h"
18 
19 #include <stddef.h>
20 #include <stdint.h>
21 
22 #include "fpdf_edit.h"
23 #include "logging.h"
24 #include "rect.h"
25 
26 #define LOG_TAG "path_object"
27 
28 namespace pdfClient {
29 
PathObject()30 PathObject::PathObject() : PageObject(Type::Path) {}
31 
CreateFPDFInstance(FPDF_DOCUMENT document,FPDF_PAGE page)32 ScopedFPDFPageObject PathObject::CreateFPDFInstance(FPDF_DOCUMENT document, FPDF_PAGE page) {
33     int segment_count = segments_.size();
34     if (segment_count == 0) {
35         return nullptr;
36     }
37     // Get Start Points
38     float x = segments_[0].x;
39     float y = segments_[0].y;
40 
41     // Create a scoped PDFium path object.
42     ScopedFPDFPageObject scoped_path_object(FPDFPageObj_CreateNewPath(x, y));
43     if (!scoped_path_object) {
44         return nullptr;
45     }
46 
47     // Insert all segments into PDFium Path Object
48     for (int i = 1; i < segment_count; ++i) {
49         // Get EndPoint for current segment.
50         x = segments_[i].x;
51         y = segments_[i].y;
52         switch (segments_[i].command) {
53             case Segment::Command::Move: {
54                 FPDFPath_MoveTo(scoped_path_object.get(), x, y);
55                 break;
56             }
57             case Segment::Command::Line: {
58                 FPDFPath_LineTo(scoped_path_object.get(), x, y);
59                 break;
60             }
61             default:
62                 break;
63         }
64         if (segments_[i].is_closed) {
65             FPDFPath_Close(scoped_path_object.get());
66         }
67     }
68 
69     // Update attributes of PDFium path object.
70     if (!UpdateFPDFInstance(scoped_path_object.get(), page)) {
71         return nullptr;
72     }
73 
74     return scoped_path_object;
75 }
76 
UpdateFPDFInstance(FPDF_PAGEOBJECT path_object,FPDF_PAGE page)77 bool PathObject::UpdateFPDFInstance(FPDF_PAGEOBJECT path_object, FPDF_PAGE page) {
78     if (!path_object) {
79         return false;
80     }
81 
82     // Check for Type Correctness.
83     if (FPDFPageObj_GetType(path_object) != FPDF_PAGEOBJ_PATH) {
84         return false;
85     }
86 
87     // Set the updated Draw Mode
88     int fill_mode = this->is_fill_ ? FPDF_FILLMODE_WINDING : FPDF_FILLMODE_NONE;
89     if (!FPDFPath_SetDrawMode(path_object, fill_mode, is_stroke_)) {
90         return false;
91     }
92 
93     // Set the updated matrix.
94     if (!SetDeviceToPageMatrix(path_object, page)) {
95         return false;
96     }
97 
98     // Set the updated Stroke Width
99     FPDFPageObj_SetStrokeWidth(path_object, stroke_width_);
100     // Set the updated Stroke Color
101     FPDFPageObj_SetStrokeColor(path_object, stroke_color_.r, stroke_color_.g, stroke_color_.b,
102                                stroke_color_.a);
103     // Set the updated Fill Color
104     FPDFPageObj_SetFillColor(path_object, fill_color_.r, fill_color_.g, fill_color_.b,
105                              fill_color_.a);
106 
107     return true;
108 }
109 
PopulateFromFPDFInstance(FPDF_PAGEOBJECT path_object,FPDF_PAGE page)110 bool PathObject::PopulateFromFPDFInstance(FPDF_PAGEOBJECT path_object, FPDF_PAGE page) {
111     // Count the number of segments in the Path
112     int segment_count = FPDFPath_CountSegments(path_object);
113     if (segment_count == 0) {
114         return false;
115     }
116 
117     // Get Path segments
118     for (int index = 0; index < segment_count; ++index) {
119         FPDF_PATHSEGMENT path_segment = FPDFPath_GetPathSegment(path_object, index);
120 
121         // Get Type for the current Path Segment
122         int type = FPDFPathSegment_GetType(path_segment);
123         if (type == FPDF_SEGMENT_UNKNOWN || type == FPDF_SEGMENT_BEZIERTO) {
124             // Control point extraction of bezier curve is not supported by Pdfium as of now.
125             return false;
126         }
127 
128         // Get EndPoint for the current Path Segment
129         float x, y;
130         FPDFPathSegment_GetPoint(path_segment, &x, &y);
131 
132         // Get Close for the current Path Segment
133         bool is_closed = FPDFPathSegment_GetClose(path_segment);
134 
135         // Add Segment to PageObject Data
136         if (type == FPDF_SEGMENT_LINETO) {
137             segments_.emplace_back(Segment::Command::Line, x, y, is_closed);
138         } else {
139             segments_.emplace_back(Segment::Command::Move, x, y, is_closed);
140         }
141     }
142 
143     // Get Draw Mode
144     int fill_mode;
145     FPDF_BOOL stroke;
146     if (!FPDFPath_GetDrawMode(path_object, &fill_mode, &stroke)) {
147         LOGE("Path GetDrawMode Failed!");
148         return false;
149     }
150     is_fill_ = fill_mode;
151     is_stroke_ = stroke;
152 
153     // Get Matrix
154     if (!GetPageToDeviceMatrix(path_object, page)) {
155         return false;
156     }
157 
158     // Get Fill Color
159     FPDFPageObj_GetFillColor(path_object, &fill_color_.r, &fill_color_.g, &fill_color_.b,
160                              &fill_color_.a);
161     // Get Stroke Color
162     FPDFPageObj_GetStrokeColor(path_object, &stroke_color_.r, &stroke_color_.g, &stroke_color_.b,
163                                &stroke_color_.a);
164     // Get Stroke Width
165     FPDFPageObj_GetStrokeWidth(path_object, &stroke_width_);
166 
167     return true;
168 }
169 
GetPageToDeviceMatrix(FPDF_PAGEOBJECT path_object,FPDF_PAGE page)170 bool PathObject::GetPageToDeviceMatrix(FPDF_PAGEOBJECT path_object, FPDF_PAGE page) {
171     Matrix page_matrix;
172     if (!FPDFPageObj_GetMatrix(path_object, reinterpret_cast<FS_MATRIX*>(&page_matrix))) {
173         LOGE("GetPageMatrix failed!");
174         return false;
175     }
176 
177     float page_height = FPDF_GetPageHeightF(page);
178 
179     // Page to device matrix.
180     device_matrix_.a = page_matrix.a;
181     device_matrix_.b = (page_matrix.b != 0) ? -page_matrix.b : 0;
182     device_matrix_.c = (page_matrix.c != 0) ? -page_matrix.c : 0;
183     device_matrix_.d = page_matrix.d;
184     device_matrix_.e = page_matrix.e + (page_height * page_matrix.c);
185     device_matrix_.f = page_height - page_matrix.f - (page_height * page_matrix.d);
186 
187     return true;
188 }
189 
SetDeviceToPageMatrix(FPDF_PAGEOBJECT path_object,FPDF_PAGE page)190 bool PathObject::SetDeviceToPageMatrix(FPDF_PAGEOBJECT path_object, FPDF_PAGE page) {
191     // Reset Previous Transformation.
192     Matrix identity = {1, 0, 0, 1, 0, 0};
193     if (!FPDFPageObj_SetMatrix(path_object, reinterpret_cast<FS_MATRIX*>(&identity))) {
194         LOGE("SetMatrix failed!");
195         return false;
196     }
197 
198     float page_height = FPDF_GetPageHeightF(page);
199 
200     FPDFPageObj_Transform(path_object, 1, 0, 0, 1, 0, -page_height);
201     FPDFPageObj_Transform(path_object, device_matrix_.a, -device_matrix_.b, -device_matrix_.c,
202                           device_matrix_.d, device_matrix_.e, -device_matrix_.f);
203     FPDFPageObj_Transform(path_object, 1, 0, 0, 1, 0, page_height);
204 
205     return true;
206 }
207 
208 PathObject::~PathObject() = default;
209 
210 }  // namespace pdfClient