• 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 "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