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 "text_object.h"
18
19 #include <stddef.h>
20 #include <stdint.h>
21 #include <utils/pdf_strings.h>
22
23 #include <string>
24
25 #include "cpp/fpdf_scopers.h"
26 #include "fpdf_edit.h"
27 #include "fpdfview.h"
28 #include "logging.h"
29
30 #define LOG_TAG "text_object"
31
32 using FontFamily = pdfClient::Font::Family;
33
34 namespace pdfClient {
35
GetFont(FPDF_PAGEOBJECT text_object)36 std::optional<Font> GetFont(FPDF_PAGEOBJECT text_object) {
37 // Get FPDF font.
38 FPDF_FONT font = FPDFTextObj_GetFont(text_object);
39
40 // Get buffer length.
41 unsigned long text_len = FPDFFont_GetBaseFontName(font, nullptr, 0);
42
43 // Get font name.
44 std::unique_ptr<char[]> p_font_name = std::make_unique<char[]>(text_len);
45 if (!FPDFFont_GetBaseFontName(font, p_font_name.get(), text_len)) {
46 LOGE("GetBaseFontName failed");
47 return std::nullopt;
48 }
49
50 // Find font index.
51 std::string font_name(p_font_name.get());
52 if (font_mapper.find(font_name) == font_mapper.end()) {
53 LOGE("Font not found in font_mapper %s", font_name.c_str());
54 return std::nullopt;
55 }
56
57 return font_mapper[font_name];
58 }
59
GetRenderMode(FPDF_TEXT_RENDERMODE render_mode)60 TextObject::RenderMode GetRenderMode(FPDF_TEXT_RENDERMODE render_mode) {
61 switch (render_mode) {
62 case FPDF_TEXTRENDERMODE_FILL: {
63 return TextObject::RenderMode::Fill;
64 }
65 case FPDF_TEXTRENDERMODE_STROKE: {
66 return TextObject::RenderMode::Stroke;
67 }
68 case FPDF_TEXTRENDERMODE_FILL_STROKE: {
69 return TextObject::RenderMode::FillStroke;
70 }
71 default: {
72 return TextObject::RenderMode::Unknown;
73 }
74 }
75 }
76
GetRenderMode(TextObject::RenderMode render_mode)77 FPDF_TEXT_RENDERMODE GetRenderMode(TextObject::RenderMode render_mode) {
78 switch (render_mode) {
79 case TextObject::RenderMode::Fill: {
80 return FPDF_TEXTRENDERMODE_FILL;
81 }
82 case TextObject::RenderMode::Stroke: {
83 return FPDF_TEXTRENDERMODE_STROKE;
84 }
85 case TextObject::RenderMode::FillStroke: {
86 return FPDF_TEXTRENDERMODE_FILL_STROKE;
87 }
88 default: {
89 return FPDF_TEXTRENDERMODE_UNKNOWN;
90 }
91 }
92 }
93
GetText(FPDF_PAGEOBJECT text_object,FPDF_PAGE page)94 std::optional<std::wstring> GetText(FPDF_PAGEOBJECT text_object, FPDF_PAGE page) {
95 // Get text page.
96 ScopedFPDFTextPage text_page(FPDFText_LoadPage(page));
97 if (!text_page) {
98 return std::nullopt;
99 }
100
101 // Get buffer length.
102 unsigned long text_len = FPDFTextObj_GetText(text_object, text_page.get(), nullptr, 0);
103
104 // Get text.
105 std::unique_ptr<FPDF_WCHAR[]> p_text_buffer = std::make_unique<FPDF_WCHAR[]>(text_len);
106 if (!FPDFTextObj_GetText(text_object, text_page.get(), p_text_buffer.get(), text_len)) {
107 LOGE("GetText failed");
108 return std::nullopt;
109 }
110
111 return pdfClient_utils::ToWideString(p_text_buffer.get(), text_len);
112 }
113
Font(const std::string & font_name,Family family,bool bold,bool italic)114 Font::Font(const std::string& font_name, Family family, bool bold, bool italic)
115 : font_name_(font_name), family_(family), bold_(bold), italic_(italic) {}
116
GetName()117 std::string Font::GetName() {
118 std::string name = font_name_;
119
120 if (bold_ && italic_) {
121 name += BoldItalic;
122 } else if (bold_) {
123 name += Bold;
124 } else if (italic_) {
125 name += Italic;
126 }
127
128 return name;
129 }
130
TextObject()131 TextObject::TextObject() : PageObject(Type::Text) {}
132
CreateFPDFInstance(FPDF_DOCUMENT document,FPDF_PAGE page)133 ScopedFPDFPageObject TextObject::CreateFPDFInstance(FPDF_DOCUMENT document, FPDF_PAGE page) {
134 // Create a scoped Pdfium font object.
135 ScopedFPDFFont font(FPDFText_LoadStandardFont(document, font_.GetName().c_str()));
136 if (!font) {
137 LOGE("Font creation failed");
138 return nullptr;
139 }
140
141 // Create a scoped Pdfium text object.
142 ScopedFPDFPageObject scoped_text_object(
143 FPDFPageObj_CreateTextObj(document, font.get(), font_size_));
144 if (!scoped_text_object) {
145 LOGE("Object creation failed");
146 return nullptr;
147 }
148
149 // Update attributes of Pdfium text object.
150 if (!UpdateFPDFInstance(scoped_text_object.get(), page)) {
151 LOGE("Create update failed");
152 return nullptr;
153 }
154
155 return scoped_text_object;
156 }
157
UpdateFPDFInstance(FPDF_PAGEOBJECT text_object,FPDF_PAGE page)158 bool TextObject::UpdateFPDFInstance(FPDF_PAGEOBJECT text_object, FPDF_PAGE page) {
159 if (!text_object) {
160 LOGE("Object NULL");
161 return false;
162 }
163
164 // Check for type correctness.
165 if (FPDFPageObj_GetType(text_object) != FPDF_PAGEOBJ_TEXT) {
166 LOGE("TypeCast failed");
167 return false;
168 }
169
170 // Set the updated text.
171 auto fpdf_text = pdfClient_utils::ToFPDFWideString(text_);
172 if (text_.size() == 0 || !FPDFText_SetText(text_object, fpdf_text.get())) {
173 LOGE("SetText failed");
174 return false;
175 }
176
177 // Set the updated text render mode.
178 if (!FPDFTextObj_SetTextRenderMode(text_object, GetRenderMode(render_mode_))) {
179 LOGE("SetTextRenderMode failed");
180 return false;
181 }
182
183 // Set the updated device matrix.
184 if (!SetDeviceToPageMatrix(text_object, page)) {
185 LOGE("SetMatrix failed");
186 return false;
187 }
188
189 // Set updated stroke width.
190 if (!FPDFPageObj_SetStrokeWidth(text_object, stroke_width_)) {
191 LOGE("SetStrokeWidth failed");
192 return false;
193 }
194
195 // Set updated stroke color.
196 if (!FPDFPageObj_SetStrokeColor(text_object, stroke_color_.r, stroke_color_.g, stroke_color_.b,
197 stroke_color_.a)) {
198 LOGE("SetStrokeColor failed");
199 return false;
200 }
201
202 // Set the updated fill color.
203 if (!FPDFPageObj_SetFillColor(text_object, fill_color_.r, fill_color_.g, fill_color_.b,
204 fill_color_.a)) {
205 LOGE("SetFillColor failed");
206 return false;
207 }
208
209 return true;
210 }
211
PopulateFromFPDFInstance(FPDF_PAGEOBJECT text_object,FPDF_PAGE page)212 bool TextObject::PopulateFromFPDFInstance(FPDF_PAGEOBJECT text_object, FPDF_PAGE page) {
213 // Get font.
214 std::optional<Font> fontOpt = GetFont(text_object);
215 if (!fontOpt) {
216 LOGE("GetFont failed");
217 return false;
218 }
219 font_ = *fontOpt;
220
221 // Get font size.
222 if (!FPDFTextObj_GetFontSize(text_object, &font_size_)) {
223 LOGE("GetFontSize failed");
224 return false;
225 }
226
227 // Get text.
228 std::optional<std::wstring> textOpt = GetText(text_object, page);
229 if (!textOpt) {
230 LOGE("GetText failed");
231 return false;
232 }
233 text_ = *textOpt;
234
235 // Get render mode.
236 render_mode_ = GetRenderMode(FPDFTextObj_GetTextRenderMode(text_object));
237 if (render_mode_ == RenderMode::Unknown) {
238 LOGE("GetRenderMode unknown");
239 return false;
240 }
241
242 // Get device matrix.
243 if (!GetPageToDeviceMatrix(text_object, page)) {
244 LOGE("GetMatrix failed");
245 return false;
246 }
247
248 // Get stroke width.
249 if (!FPDFPageObj_GetStrokeWidth(text_object, &stroke_width_)) {
250 LOGE("GetStrokeWidth failed");
251 return false;
252 }
253
254 // Get stroke color.
255 if (!FPDFPageObj_GetStrokeColor(text_object, &stroke_color_.r, &stroke_color_.g,
256 &stroke_color_.b, &stroke_color_.a)) {
257 LOGE("GetStrokeColor failed");
258 return false;
259 }
260
261 // Get fill color.
262 if (!FPDFPageObj_GetFillColor(text_object, &fill_color_.r, &fill_color_.g, &fill_color_.b,
263 &fill_color_.a)) {
264 LOGE("GetFillColor failed");
265 return false;
266 }
267
268 return true;
269 }
270
271 TextObject::~TextObject() = default;
272
273 // Font Mapper
274 std::unordered_map<std::string, Font> font_mapper = {
275 {Courier, Font(Courier, FontFamily::Courier)},
276 {Courier + Bold, Font(Courier, FontFamily::Courier, true)},
277 {Courier + Oblique, Font(Courier, FontFamily::Courier, false, true)},
278 {Courier + BoldOblique, Font(Courier, FontFamily::Courier, true, true)},
279
280 {Helvetica, Font(Helvetica, FontFamily::Helvetica)},
281 {Helvetica + Bold, Font(Helvetica, FontFamily::Helvetica, true)},
282 {Helvetica + Oblique, Font(Helvetica, FontFamily::Helvetica, false, true)},
283 {Helvetica + BoldOblique, Font(Helvetica, FontFamily::Helvetica, true, true)},
284
285 {TimesRoman, Font(TimesRoman, FontFamily::TimesRoman)},
286 {Times + Bold, Font(Times, FontFamily::TimesRoman, true)},
287 {Times + Italic, Font(Times, FontFamily::TimesRoman, false, true)},
288 {Times + BoldItalic, Font(Times, FontFamily::TimesRoman, true, true)},
289
290 {Symbol, Font(Symbol, FontFamily::Symbol)}};
291
292 // Standard Font Names.
293 std::vector<std::string> font_names = {CourierNew, Helvetica, Symbol, TimesNewRoman};
294
295 } // namespace pdfClient