pdfium_render/pdf/document/page/object/
text.rs1use crate::bindgen::{
5 FPDF_DOCUMENT, FPDF_FONT, FPDF_PAGEOBJECT, FPDF_TEXT_RENDERMODE,
6 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_CLIP, FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL,
7 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL_CLIP,
8 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL_STROKE,
9 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL_STROKE_CLIP,
10 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_INVISIBLE,
11 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_STROKE,
12 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_STROKE_CLIP,
13 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_UNKNOWN, FPDF_WCHAR,
14};
15use crate::bindings::PdfiumLibraryBindings;
16use crate::error::{PdfiumError, PdfiumInternalError};
17use crate::pdf::document::fonts::ToPdfFontToken;
18use crate::pdf::document::page::object::private::internal::PdfPageObjectPrivate;
19use crate::pdf::document::page::object::PdfPageObjectOwnership;
20use crate::pdf::document::PdfDocument;
21use crate::pdf::font::PdfFont;
22use crate::pdf::matrix::{PdfMatrix, PdfMatrixValue};
23use crate::pdf::points::PdfPoints;
24use crate::pdfium::PdfiumLibraryBindingsAccessor;
25use crate::utils::mem::create_byte_buffer;
26use crate::utils::utf16le::get_string_from_pdfium_utf16le_bytes;
27use crate::{create_transform_getters, create_transform_setters};
28use std::marker::PhantomData;
29
30#[cfg(any(
31 feature = "pdfium_future",
32 feature = "pdfium_7763",
33 feature = "pdfium_7543",
34 feature = "pdfium_7350",
35 feature = "pdfium_7215",
36 feature = "pdfium_7123",
37 feature = "pdfium_6996",
38 feature = "pdfium_6721",
39 feature = "pdfium_6666",
40 feature = "pdfium_6611",
41))]
42use {
43 crate::pdf::document::page::text::chars::PdfPageTextChars,
44 crate::pdf::document::page::text::PdfPageText,
45};
46
47#[cfg(doc)]
48use {
49 crate::pdf::document::page::object::PdfPageObject,
50 crate::pdf::document::page::object::PdfPageObjectType,
51 crate::pdf::document::page::objects::common::PdfPageObjectsCommon,
52 crate::pdf::document::page::PdfPage,
53};
54
55#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
58pub enum PdfPageTextRenderMode {
59 Unknown = FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_UNKNOWN as isize,
61
62 FilledUnstroked = FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL as isize,
64
65 StrokedUnfilled = FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_STROKE as isize,
67
68 FilledThenStroked = FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL_STROKE as isize,
70
71 Invisible = FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_INVISIBLE as isize,
73
74 FilledUnstrokedClipping = FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL_CLIP as isize,
76
77 StrokedUnfilledClipping = FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_STROKE_CLIP as isize,
79
80 FilledThenStrokedClipping = FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL_STROKE_CLIP as isize,
82
83 InvisibleClipping = FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_CLIP as isize,
85}
86
87impl PdfPageTextRenderMode {
88 #[inline]
89 pub(crate) fn from_pdfium(value: i32) -> Result<PdfPageTextRenderMode, PdfiumError> {
90 match value {
91 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_UNKNOWN => Ok(PdfPageTextRenderMode::Unknown),
92 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL => {
93 Ok(PdfPageTextRenderMode::FilledUnstroked)
94 }
95 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_STROKE => {
96 Ok(PdfPageTextRenderMode::StrokedUnfilled)
97 }
98 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL_STROKE => {
99 Ok(PdfPageTextRenderMode::FilledThenStroked)
100 }
101 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_INVISIBLE => {
102 Ok(PdfPageTextRenderMode::Invisible)
103 }
104 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL_CLIP => {
105 Ok(PdfPageTextRenderMode::FilledUnstrokedClipping)
106 }
107 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_STROKE_CLIP => {
108 Ok(PdfPageTextRenderMode::StrokedUnfilledClipping)
109 }
110 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL_STROKE_CLIP => {
111 Ok(PdfPageTextRenderMode::FilledThenStrokedClipping)
112 }
113 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_CLIP => {
114 Ok(PdfPageTextRenderMode::InvisibleClipping)
115 }
116 _ => Err(PdfiumError::UnknownPdfPageTextRenderMode),
117 }
118 }
119
120 #[inline]
121 #[allow(dead_code)]
122 pub(crate) fn as_pdfium(&self) -> FPDF_TEXT_RENDERMODE {
124 match self {
125 PdfPageTextRenderMode::Unknown => FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_UNKNOWN,
126 PdfPageTextRenderMode::FilledUnstroked => FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL,
127 PdfPageTextRenderMode::StrokedUnfilled => {
128 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_STROKE
129 }
130 PdfPageTextRenderMode::FilledThenStroked => {
131 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL_STROKE
132 }
133 PdfPageTextRenderMode::Invisible => FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_INVISIBLE,
134 PdfPageTextRenderMode::FilledUnstrokedClipping => {
135 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL_CLIP
136 }
137 PdfPageTextRenderMode::StrokedUnfilledClipping => {
138 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_STROKE_CLIP
139 }
140 PdfPageTextRenderMode::FilledThenStrokedClipping => {
141 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_FILL_STROKE_CLIP
142 }
143 PdfPageTextRenderMode::InvisibleClipping => {
144 FPDF_TEXT_RENDERMODE_FPDF_TEXTRENDERMODE_CLIP
145 }
146 }
147 }
148}
149
150pub struct PdfPageTextObject<'a> {
167 object_handle: FPDF_PAGEOBJECT,
168 ownership: PdfPageObjectOwnership,
169 lifetime: PhantomData<&'a FPDF_PAGEOBJECT>,
170}
171
172impl<'a> PdfPageTextObject<'a> {
173 #[inline]
174 pub(crate) fn from_pdfium(
175 object_handle: FPDF_PAGEOBJECT,
176 ownership: PdfPageObjectOwnership,
177 ) -> Self {
178 PdfPageTextObject {
179 object_handle,
180 ownership,
181 lifetime: PhantomData,
182 }
183 }
184
185 #[inline]
196 pub fn new(
197 document: &PdfDocument<'a>,
198 text: impl ToString,
199 font: impl ToPdfFontToken,
200 font_size: PdfPoints,
201 ) -> Result<Self, PdfiumError> {
202 Self::new_from_handles(
203 document.handle(),
204 text,
205 font.token().handle(),
206 font_size,
207 document.bindings(),
208 )
209 }
210
211 pub(crate) fn new_from_handles(
214 document: FPDF_DOCUMENT,
215 text: impl ToString,
216 font: FPDF_FONT,
217 font_size: PdfPoints,
218 bindings: &'a dyn PdfiumLibraryBindings,
219 ) -> Result<Self, PdfiumError> {
220 let handle = unsafe { bindings.FPDFPageObj_CreateTextObj(document, font, font_size.value) };
221
222 if handle.is_null() {
223 Err(PdfiumError::PdfiumLibraryInternalError(
224 PdfiumInternalError::Unknown,
225 ))
226 } else {
227 let mut result = PdfPageTextObject {
228 object_handle: handle,
229 ownership: PdfPageObjectOwnership::unowned(),
230 lifetime: PhantomData,
231 };
232
233 result.set_text(text)?;
234
235 Ok(result)
236 }
237 }
238
239 pub fn render_mode(&self) -> PdfPageTextRenderMode {
241 PdfPageTextRenderMode::from_pdfium(unsafe {
242 self.bindings()
243 .FPDFTextObj_GetTextRenderMode(self.object_handle)
244 })
245 .unwrap_or(PdfPageTextRenderMode::Unknown)
246 }
247
248 #[inline]
252 pub fn is_visible(&self) -> bool {
253 match self.render_mode() {
254 PdfPageTextRenderMode::Invisible | PdfPageTextRenderMode::InvisibleClipping => false,
255 _ => true,
256 }
257 }
258
259 #[inline]
266 pub fn scaled_font_size(&self) -> PdfPoints {
267 PdfPoints::new(self.unscaled_font_size().value * self.get_vertical_scale())
268 }
269
270 pub fn unscaled_font_size(&self) -> PdfPoints {
277 let mut result = 0.0;
278
279 if self.bindings().is_true(unsafe {
280 self.bindings()
281 .FPDFTextObj_GetFontSize(self.object_handle, &mut result)
282 }) {
283 PdfPoints::new(result)
284 } else {
285 PdfPoints::ZERO
286 }
287 }
288
289 pub fn font(&self) -> PdfFont<'_> {
291 PdfFont::from_pdfium(
292 unsafe { self.bindings().FPDFTextObj_GetFont(self.object_handle) },
293 None,
294 false,
295 )
296 }
297
298 pub fn text(&self) -> String {
319 let page_handle = match self.ownership() {
329 PdfPageObjectOwnership::Page(ownership) => Some(ownership.page_handle()),
330 PdfPageObjectOwnership::AttachedAnnotation(ownership) => Some(ownership.page_handle()),
331 _ => None,
332 };
333
334 if let Some(page_handle) = page_handle {
335 let text_handle = unsafe { self.bindings().FPDFText_LoadPage(page_handle) };
336
337 if !text_handle.is_null() {
338 let buffer_length = unsafe {
339 self.bindings().FPDFTextObj_GetText(
340 self.object_handle(),
341 text_handle,
342 std::ptr::null_mut(),
343 0,
344 )
345 };
346
347 if buffer_length == 0 {
348 return String::new();
351 }
352
353 let mut buffer = create_byte_buffer(buffer_length as usize);
354
355 let result = unsafe {
356 self.bindings().FPDFTextObj_GetText(
357 self.object_handle(),
358 text_handle,
359 buffer.as_mut_ptr() as *mut FPDF_WCHAR,
360 buffer_length,
361 )
362 };
363
364 assert_eq!(result, buffer_length);
365
366 unsafe {
367 self.bindings().FPDFText_ClosePage(text_handle);
368 }
369
370 get_string_from_pdfium_utf16le_bytes(buffer).unwrap_or_default()
371 } else {
372 String::new()
376 }
377 } else {
378 String::new()
381 }
382 }
383
384 pub fn set_text(&mut self, text: impl ToString) -> Result<(), PdfiumError> {
389 let text = text.to_string();
390
391 let text = if text.is_empty() { " " } else { text.as_str() };
392
393 if self.bindings().is_true(unsafe {
394 self.bindings()
395 .FPDFText_SetText_str(self.object_handle(), text)
396 }) {
397 Ok(())
398 } else {
399 Err(PdfiumError::PdfiumLibraryInternalError(
400 PdfiumInternalError::Unknown,
401 ))
402 }
403 }
404
405 pub fn set_render_mode(
407 &mut self,
408 render_mode: PdfPageTextRenderMode,
409 ) -> Result<(), PdfiumError> {
410 if self.bindings().is_true(unsafe {
411 self.bindings()
412 .FPDFTextObj_SetTextRenderMode(self.object_handle(), render_mode.as_pdfium())
413 }) {
414 Ok(())
415 } else {
416 Err(PdfiumError::PdfiumLibraryInternalError(
417 PdfiumInternalError::Unknown,
418 ))
419 }
420 }
421
422 #[cfg(any(
423 feature = "pdfium_future",
424 feature = "pdfium_7763",
425 feature = "pdfium_7543",
426 feature = "pdfium_7350",
427 feature = "pdfium_7215",
428 feature = "pdfium_7123",
429 feature = "pdfium_6996",
430 feature = "pdfium_6721",
431 feature = "pdfium_6666",
432 feature = "pdfium_6611",
433 ))]
434 #[inline]
437 pub fn chars(&self, text: &'a PdfPageText<'a>) -> Result<PdfPageTextChars<'a>, PdfiumError> {
438 text.chars_for_object(self)
439 }
440
441 #[cfg(any(
442 feature = "pdfium_future",
443 feature = "pdfium_7763",
444 feature = "pdfium_7543",
445 feature = "pdfium_7350",
446 feature = "pdfium_7215",
447 feature = "pdfium_7123",
448 feature = "pdfium_6996",
449 feature = "pdfium_6721",
450 feature = "pdfium_6666",
451 feature = "pdfium_6611",
452 ))]
453 #[inline]
458 pub fn has_descenders(&self, text: &PdfPageText) -> Result<bool, PdfiumError> {
459 self.chars(text)
460 .map(|chars| chars.iter().any(|char| char.has_descender()))
461 }
462
463 #[cfg(any(
464 feature = "pdfium_future",
465 feature = "pdfium_7763",
466 feature = "pdfium_7543",
467 feature = "pdfium_7350",
468 feature = "pdfium_7215",
469 feature = "pdfium_7123",
470 feature = "pdfium_6996",
471 feature = "pdfium_6721",
472 feature = "pdfium_6666",
473 feature = "pdfium_6611",
474 ))]
475 pub fn descent(&self, text: &PdfPageText) -> Result<PdfPoints, PdfiumError> {
481 let object_bottom = self.get_vertical_translation();
482
483 let mut maximum_descent = object_bottom;
484
485 for char in self.chars(text)?.iter() {
486 let char_bottom = char.tight_bounds()?.bottom();
487
488 if char_bottom < maximum_descent {
489 maximum_descent = char_bottom;
490 }
491 }
492
493 Ok(maximum_descent - object_bottom)
494 }
495
496 create_transform_setters!(
497 &mut Self,
498 Result<(), PdfiumError>,
499 "this [PdfPageTextObject]",
500 "this [PdfPageTextObject].",
501 "this [PdfPageTextObject],"
502 );
503
504 create_transform_getters!(
508 "this [PdfPageTextObject]",
509 "this [PdfPageTextObject].",
510 "this [PdfPageTextObject],"
511 );
512
513 }
516
517impl<'a> PdfPageObjectPrivate<'a> for PdfPageTextObject<'a> {
518 #[inline]
519 fn object_handle(&self) -> FPDF_PAGEOBJECT {
520 self.object_handle
521 }
522
523 #[inline]
524 fn ownership(&self) -> &PdfPageObjectOwnership {
525 &self.ownership
526 }
527
528 #[inline]
529 fn set_ownership(&mut self, ownership: PdfPageObjectOwnership) {
530 self.ownership = ownership;
531 }
532}
533
534impl<'a> Drop for PdfPageTextObject<'a> {
535 fn drop(&mut self) {
537 self.drop_impl();
538 }
539}
540
541impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfPageTextObject<'a> {}
542
543#[cfg(feature = "thread_safe")]
544unsafe impl<'a> Send for PdfPageTextObject<'a> {}
545
546#[cfg(feature = "thread_safe")]
547unsafe impl<'a> Sync for PdfPageTextObject<'a> {}