1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use crate::bindgen::{FPDFBitmap_BGRA, FPDF_PAGE};
use crate::bindings::PdfiumLibraryBindings;
use crate::bitmap::{PdfBitmap, PdfBitmapFormat, PdfBitmapRotation};
use crate::bitmap_config::PdfBitmapConfig;
use crate::{PdfPageIndex, PdfPoints, PdfiumError, PdfiumInternalError};

/// The orientation of a PdfPage.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum PdfPageOrientation {
    Portrait,
    Landscape,
}

impl PdfPageOrientation {
    #[inline]
    pub(crate) fn from_width_and_height(width: PdfPoints, height: PdfPoints) -> Self {
        if width > height {
            PdfPageOrientation::Landscape
        } else {
            PdfPageOrientation::Portrait
        }
    }
}

/// A single page in a PdfDocument.
pub struct PdfPage<'a> {
    index: PdfPageIndex,
    handle: FPDF_PAGE,
    bindings: &'a dyn PdfiumLibraryBindings,
}

impl<'a> PdfPage<'a> {
    #[inline]
    pub(crate) fn from_pdfium(
        index: PdfPageIndex,
        handle: FPDF_PAGE,
        bindings: &'a dyn PdfiumLibraryBindings,
    ) -> Self {
        PdfPage {
            index,
            handle,
            bindings,
        }
    }

    /// Returns the zero-based page index of this PdfPage in its containing PdfDocument.
    #[inline]
    pub fn index(&self) -> PdfPageIndex {
        self.index
    }

    /// Returns the width of this PdfPage in points.
    pub fn width(&self) -> PdfPoints {
        self.bindings.FPDF_GetPageWidthF(self.handle)
    }

    /// Returns the height of this PdfPage in points.
    pub fn height(&self) -> PdfPoints {
        self.bindings.FPDF_GetPageHeightF(self.handle)
    }

    /// Returns [PdfPageOrientation::Landscape] if the width of this PdfPage
    /// is greater than its height; otherwise returns [PdfPageOrientation::Portrait].
    #[inline]
    pub fn orientation(&self) -> PdfPageOrientation {
        PdfPageOrientation::from_width_and_height(self.width(), self.height())
    }

    /// Returns true if this PdfPage has orientation [PdfPageOrientation::Portrait].
    #[inline]
    pub fn is_portrait(&self) -> bool {
        self.orientation() == PdfPageOrientation::Portrait
    }

    /// Returns true if this PdfPage has orientation [PdfPageOrientation::Landscape].
    #[inline]
    pub fn is_landscape(&self) -> bool {
        self.orientation() == PdfPageOrientation::Landscape
    }

    /// Returns a PdfBitmap using sizing and rotation settings configured in the given
    /// PdfBitmapConfig.
    ///
    /// See also [PdfPage::get_bitmap()], which directly creates a PdfBitmap object from this page
    /// using caller-specified pixel dimensions and page rotation settings.
    #[inline]
    pub fn get_bitmap_with_config(
        &self,
        config: &PdfBitmapConfig,
    ) -> Result<PdfBitmap, PdfiumError> {
        let (width, height, rotation) = config.apply_to_page(self);

        self.get_bitmap(width, height, rotation)
    }

    /// Returns a PdfBitmap with the given pixel dimensions and render-time rotation
    /// for this PdfPage containing the rendered content of this PdfPage.
    ///
    /// It is the responsibility of the caller to ensure the given pixel width and height
    /// correctly maintain the page's aspect ratio.
    ///
    /// See also [PdfPage::get_bitmap_with_config()], which calculates the correct pixel dimensions
    /// and rotation settings to use from a PdfBitmapConfig object.
    pub fn get_bitmap(
        &self,
        width: u16,
        height: u16,
        rotation: Option<PdfBitmapRotation>,
    ) -> Result<PdfBitmap, PdfiumError> {
        let handle = self.bindings.FPDFBitmap_CreateEx(
            width as i32,
            height as i32,
            FPDFBitmap_BGRA as i32,
            std::ptr::null_mut(),
            0, // Not relevant because pdfium will create the buffer itself.
        );

        if handle.is_null() {
            if let Some(error) = self.bindings.get_pdfium_last_error() {
                Err(PdfiumError::PdfiumLibraryInternalError(error))
            } else {
                // This would be an unusual situation; a null handle indicating failure,
                // yet pdfium's error code indicates success.

                Err(PdfiumError::PdfiumLibraryInternalError(
                    PdfiumInternalError::Unknown,
                ))
            }
        } else {
            Ok(PdfBitmap::from_pdfium(
                width,
                height,
                PdfBitmapFormat::from_pdfium(FPDFBitmap_BGRA)?,
                rotation.unwrap_or(PdfBitmapRotation::None),
                handle,
                &self.handle,
                self.bindings,
            ))
        }
    }
}

impl<'a> Drop for PdfPage<'a> {
    /// Closes the PdfPage, releasing held memory.
    #[inline]
    fn drop(&mut self) {
        self.bindings.FPDF_ClosePage(self.handle);
    }
}