use crate::bindgen::{
FPDF_BOOL, FPDF_FILLMODE_ALTERNATE, FPDF_FILLMODE_NONE, FPDF_FILLMODE_WINDING, FPDF_PAGEOBJECT,
};
use crate::bindings::PdfiumLibraryBindings;
use crate::error::{PdfiumError, PdfiumInternalError};
use crate::pdf::color::PdfColor;
use crate::pdf::document::page::object::private::internal::PdfPageObjectPrivate;
use crate::pdf::document::page::object::{PdfPageObjectCommon, PdfPageObjectOwnership};
use crate::pdf::document::PdfDocument;
use crate::pdf::matrix::{PdfMatrix, PdfMatrixValue};
use crate::pdf::path::segment::PdfPathSegment;
use crate::pdf::path::segments::{PdfPathSegmentIndex, PdfPathSegments, PdfPathSegmentsIterator};
use crate::pdf::points::PdfPoints;
use crate::pdf::rect::PdfRect;
use crate::pdfium::PdfiumLibraryBindingsAccessor;
use crate::{create_transform_getters, create_transform_setters};
use std::convert::TryInto;
use std::marker::PhantomData;
use std::os::raw::{c_int, c_uint};
#[cfg(doc)]
use {
crate::pdf::document::page::object::PdfPageObject,
crate::pdf::document::page::object::PdfPageObjectType,
crate::pdf::document::page::objects::common::PdfPageObjectsCommon,
crate::pdf::document::page::PdfPage,
};
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum PdfPathFillMode {
None = FPDF_FILLMODE_NONE as isize,
EvenOdd = FPDF_FILLMODE_ALTERNATE as isize,
Winding = FPDF_FILLMODE_WINDING as isize,
}
impl PdfPathFillMode {
#[inline]
pub(crate) fn from_pdfium(value: c_int) -> Result<PdfPathFillMode, PdfiumError> {
match value as u32 {
FPDF_FILLMODE_NONE => Ok(PdfPathFillMode::None),
FPDF_FILLMODE_ALTERNATE => Ok(PdfPathFillMode::EvenOdd),
FPDF_FILLMODE_WINDING => Ok(PdfPathFillMode::Winding),
_ => Err(PdfiumError::UnknownPdfPagePathFillMode),
}
}
#[inline]
#[allow(dead_code)]
pub(crate) fn as_pdfium(&self) -> c_uint {
match self {
PdfPathFillMode::None => FPDF_FILLMODE_NONE,
PdfPathFillMode::EvenOdd => FPDF_FILLMODE_ALTERNATE,
PdfPathFillMode::Winding => FPDF_FILLMODE_WINDING,
}
}
}
impl Default for PdfPathFillMode {
#[inline]
fn default() -> Self {
PdfPathFillMode::Winding
}
}
pub struct PdfPagePathObject<'a> {
object_handle: FPDF_PAGEOBJECT,
ownership: PdfPageObjectOwnership,
current_point_x: PdfPoints,
current_point_y: PdfPoints,
lifetime: PhantomData<&'a FPDF_PAGEOBJECT>,
}
impl<'a> PdfPagePathObject<'a> {
#[inline]
pub(crate) fn from_pdfium(
object_handle: FPDF_PAGEOBJECT,
ownership: PdfPageObjectOwnership,
) -> Self {
PdfPagePathObject {
object_handle,
ownership,
current_point_x: PdfPoints::ZERO,
current_point_y: PdfPoints::ZERO,
lifetime: PhantomData,
}
}
#[inline]
pub fn new(
document: &PdfDocument<'a>,
x: PdfPoints,
y: PdfPoints,
stroke_color: Option<PdfColor>,
stroke_width: Option<PdfPoints>,
fill_color: Option<PdfColor>,
) -> Result<Self, PdfiumError> {
Self::new_from_bindings(
document.bindings(),
x,
y,
stroke_color,
stroke_width,
fill_color,
)
}
pub(crate) fn new_from_bindings(
bindings: &'a dyn PdfiumLibraryBindings,
x: PdfPoints,
y: PdfPoints,
stroke_color: Option<PdfColor>,
stroke_width: Option<PdfPoints>,
fill_color: Option<PdfColor>,
) -> Result<Self, PdfiumError> {
let handle = unsafe { bindings.FPDFPageObj_CreateNewPath(x.value, y.value) };
if handle.is_null() {
Err(PdfiumError::PdfiumLibraryInternalError(
PdfiumInternalError::Unknown,
))
} else {
let mut result = PdfPagePathObject {
object_handle: handle,
ownership: PdfPageObjectOwnership::unowned(),
current_point_x: x,
current_point_y: y,
lifetime: PhantomData,
};
result.move_to(x, y)?;
let do_stroke = if let Some(stroke_color) = stroke_color {
if let Some(stroke_width) = stroke_width {
result.set_stroke_color(stroke_color)?;
result.set_stroke_width(stroke_width)?;
true
} else {
false
}
} else {
false
};
let fill_mode = if let Some(fill_color) = fill_color {
result.set_fill_color(fill_color)?;
PdfPathFillMode::default()
} else {
PdfPathFillMode::None
};
result.set_fill_and_stroke_mode(fill_mode, do_stroke)?;
Ok(result)
}
}
#[inline]
pub(crate) fn new_line_from_bindings(
bindings: &'a dyn PdfiumLibraryBindings,
x1: PdfPoints,
y1: PdfPoints,
x2: PdfPoints,
y2: PdfPoints,
stroke_color: PdfColor,
stroke_width: PdfPoints,
) -> Result<Self, PdfiumError> {
let mut result = Self::new_from_bindings(
bindings,
x1,
y1,
Some(stroke_color),
Some(stroke_width),
None,
)?;
result.line_to(x2, y2)?;
Ok(result)
}
#[inline]
pub fn new_line(
document: &PdfDocument<'a>,
x1: PdfPoints,
y1: PdfPoints,
x2: PdfPoints,
y2: PdfPoints,
stroke_color: PdfColor,
stroke_width: PdfPoints,
) -> Result<Self, PdfiumError> {
Self::new_line_from_bindings(
document.bindings(),
x1,
y1,
x2,
y2,
stroke_color,
stroke_width,
)
}
#[allow(clippy::too_many_arguments)]
#[inline]
pub(crate) fn new_bezier_from_bindings(
bindings: &'a dyn PdfiumLibraryBindings,
x1: PdfPoints,
y1: PdfPoints,
x2: PdfPoints,
y2: PdfPoints,
control1_x: PdfPoints,
control1_y: PdfPoints,
control2_x: PdfPoints,
control2_y: PdfPoints,
stroke_color: PdfColor,
stroke_width: PdfPoints,
) -> Result<Self, PdfiumError> {
let mut result = Self::new_from_bindings(
bindings,
x1,
y1,
Some(stroke_color),
Some(stroke_width),
None,
)?;
result.bezier_to(x2, y2, control1_x, control1_y, control2_x, control2_y)?;
Ok(result)
}
#[allow(clippy::too_many_arguments)]
#[inline]
pub fn new_bezier(
document: &PdfDocument<'a>,
x1: PdfPoints,
y1: PdfPoints,
x2: PdfPoints,
y2: PdfPoints,
control1_x: PdfPoints,
control1_y: PdfPoints,
control2_x: PdfPoints,
control2_y: PdfPoints,
stroke_color: PdfColor,
stroke_width: PdfPoints,
) -> Result<Self, PdfiumError> {
Self::new_bezier_from_bindings(
document.bindings(),
x1,
y1,
x2,
y2,
control1_x,
control1_y,
control2_x,
control2_y,
stroke_color,
stroke_width,
)
}
#[inline]
pub(crate) fn new_rect_from_bindings(
bindings: &'a dyn PdfiumLibraryBindings,
rect: PdfRect,
stroke_color: Option<PdfColor>,
stroke_width: Option<PdfPoints>,
fill_color: Option<PdfColor>,
) -> Result<Self, PdfiumError> {
let mut result = Self::new_from_bindings(
bindings,
rect.left(),
rect.bottom(),
stroke_color,
stroke_width,
fill_color,
)?;
result.rect_to(rect.right(), rect.top())?;
Ok(result)
}
#[inline]
pub fn new_rect(
document: &PdfDocument<'a>,
rect: PdfRect,
stroke_color: Option<PdfColor>,
stroke_width: Option<PdfPoints>,
fill_color: Option<PdfColor>,
) -> Result<Self, PdfiumError> {
Self::new_rect_from_bindings(
document.bindings(),
rect,
stroke_color,
stroke_width,
fill_color,
)
}
#[inline]
pub(crate) fn new_circle_from_bindings(
bindings: &'a dyn PdfiumLibraryBindings,
rect: PdfRect,
stroke_color: Option<PdfColor>,
stroke_width: Option<PdfPoints>,
fill_color: Option<PdfColor>,
) -> Result<Self, PdfiumError> {
let mut result = Self::new_from_bindings(
bindings,
rect.left(),
rect.bottom(),
stroke_color,
stroke_width,
fill_color,
)?;
result.circle_to(rect.right(), rect.top())?;
Ok(result)
}
#[inline]
pub fn new_circle(
document: &PdfDocument<'a>,
rect: PdfRect,
stroke_color: Option<PdfColor>,
stroke_width: Option<PdfPoints>,
fill_color: Option<PdfColor>,
) -> Result<Self, PdfiumError> {
Self::new_circle_from_bindings(
document.bindings(),
rect,
stroke_color,
stroke_width,
fill_color,
)
}
#[inline]
pub(crate) fn new_circle_at_from_bindings(
bindings: &'a dyn PdfiumLibraryBindings,
center_x: PdfPoints,
center_y: PdfPoints,
radius: PdfPoints,
stroke_color: Option<PdfColor>,
stroke_width: Option<PdfPoints>,
fill_color: Option<PdfColor>,
) -> Result<Self, PdfiumError> {
Self::new_circle_from_bindings(
bindings,
PdfRect::new(
center_y - radius,
center_x - radius,
center_y + radius,
center_x + radius,
),
stroke_color,
stroke_width,
fill_color,
)
}
#[inline]
pub fn new_circle_at(
document: &PdfDocument<'a>,
center_x: PdfPoints,
center_y: PdfPoints,
radius: PdfPoints,
stroke_color: Option<PdfColor>,
stroke_width: Option<PdfPoints>,
fill_color: Option<PdfColor>,
) -> Result<Self, PdfiumError> {
Self::new_circle_at_from_bindings(
document.bindings(),
center_x,
center_y,
radius,
stroke_color,
stroke_width,
fill_color,
)
}
#[inline]
pub(crate) fn new_ellipse_from_bindings(
bindings: &'a dyn PdfiumLibraryBindings,
rect: PdfRect,
stroke_color: Option<PdfColor>,
stroke_width: Option<PdfPoints>,
fill_color: Option<PdfColor>,
) -> Result<Self, PdfiumError> {
let mut result = Self::new_from_bindings(
bindings,
rect.left(),
rect.bottom(),
stroke_color,
stroke_width,
fill_color,
)?;
result.ellipse_to(rect.right(), rect.top())?;
Ok(result)
}
#[inline]
pub fn new_ellipse(
document: &PdfDocument<'a>,
rect: PdfRect,
stroke_color: Option<PdfColor>,
stroke_width: Option<PdfPoints>,
fill_color: Option<PdfColor>,
) -> Result<Self, PdfiumError> {
Self::new_ellipse_from_bindings(
document.bindings(),
rect,
stroke_color,
stroke_width,
fill_color,
)
}
#[allow(clippy::too_many_arguments)]
#[inline]
pub(crate) fn new_ellipse_at_from_bindings(
bindings: &'a dyn PdfiumLibraryBindings,
center_x: PdfPoints,
center_y: PdfPoints,
x_radius: PdfPoints,
y_radius: PdfPoints,
stroke_color: Option<PdfColor>,
stroke_width: Option<PdfPoints>,
fill_color: Option<PdfColor>,
) -> Result<Self, PdfiumError> {
Self::new_ellipse_from_bindings(
bindings,
PdfRect::new(
center_y - y_radius,
center_x - x_radius,
center_y + y_radius,
center_x + x_radius,
),
stroke_color,
stroke_width,
fill_color,
)
}
#[allow(clippy::too_many_arguments)]
#[inline]
pub fn new_ellipse_at(
document: &PdfDocument<'a>,
center_x: PdfPoints,
center_y: PdfPoints,
x_radius: PdfPoints,
y_radius: PdfPoints,
stroke_color: Option<PdfColor>,
stroke_width: Option<PdfPoints>,
fill_color: Option<PdfColor>,
) -> Result<Self, PdfiumError> {
Self::new_ellipse_at_from_bindings(
document.bindings(),
center_x,
center_y,
x_radius,
y_radius,
stroke_color,
stroke_width,
fill_color,
)
}
pub fn move_to(&mut self, x: PdfPoints, y: PdfPoints) -> Result<(), PdfiumError> {
if self.bindings().is_true(unsafe {
self.bindings()
.FPDFPath_MoveTo(self.object_handle(), x.value, y.value)
}) {
self.current_point_x = x;
self.current_point_y = y;
Ok(())
} else {
Err(PdfiumError::PdfiumLibraryInternalError(
PdfiumInternalError::Unknown,
))
}
}
pub fn line_to(&mut self, x: PdfPoints, y: PdfPoints) -> Result<(), PdfiumError> {
if self.bindings().is_true(unsafe {
self.bindings()
.FPDFPath_LineTo(self.object_handle(), x.value, y.value)
}) {
self.current_point_x = x;
self.current_point_y = y;
Ok(())
} else {
Err(PdfiumError::PdfiumLibraryInternalError(
PdfiumInternalError::Unknown,
))
}
}
pub fn bezier_to(
&mut self,
x: PdfPoints,
y: PdfPoints,
control1_x: PdfPoints,
control1_y: PdfPoints,
control2_x: PdfPoints,
control2_y: PdfPoints,
) -> Result<(), PdfiumError> {
if self.bindings().is_true(unsafe {
self.bindings().FPDFPath_BezierTo(
self.object_handle(),
control1_x.value,
control1_y.value,
control2_x.value,
control2_y.value,
x.value,
y.value,
)
}) {
self.current_point_x = x;
self.current_point_y = y;
Ok(())
} else {
Err(PdfiumError::PdfiumLibraryInternalError(
PdfiumInternalError::Unknown,
))
}
}
pub fn rect_to(&mut self, x: PdfPoints, y: PdfPoints) -> Result<(), PdfiumError> {
let orig_x = self.current_point_x;
let orig_y = self.current_point_y;
self.close_path()?;
self.line_to(orig_x, y)?;
self.line_to(x, y)?;
self.line_to(x, orig_y)?;
self.close_path()?;
self.move_to(x, y)
}
pub fn ellipse_to(&mut self, x: PdfPoints, y: PdfPoints) -> Result<(), PdfiumError> {
let x_radius = (x - self.current_point_x) / 2.0;
let y_radius = (y - self.current_point_y) / 2.0;
self.close_path()?;
self.move_to(
self.current_point_x + x_radius,
self.current_point_y + y_radius,
)?;
self.ellipse(x_radius, y_radius)?;
self.move_to(x, y)
}
pub fn circle_to(&mut self, x: PdfPoints, y: PdfPoints) -> Result<(), PdfiumError> {
let radius = (x - self.current_point_x) / 2.0;
self.move_to(self.current_point_x + radius, self.current_point_y + radius)?;
self.ellipse(radius, radius)?;
self.move_to(x, y)
}
fn ellipse(&mut self, x_radius: PdfPoints, y_radius: PdfPoints) -> Result<(), PdfiumError> {
const C: f32 = 0.551915;
let x_c = x_radius * C;
let y_c = y_radius * C;
let orig_x = self.current_point_x;
let orig_y = self.current_point_y;
self.move_to(orig_x - x_radius, orig_y)?;
self.bezier_to(
orig_x,
orig_y + y_radius,
orig_x - x_radius,
orig_y + y_c,
orig_x - x_c,
orig_y + y_radius,
)?;
self.bezier_to(
orig_x + x_radius,
orig_y,
orig_x + x_c,
orig_y + y_radius,
orig_x + x_radius,
orig_y + y_c,
)?;
self.bezier_to(
orig_x,
orig_y - y_radius,
orig_x + x_radius,
orig_y - y_c,
orig_x + x_c,
orig_y - y_radius,
)?;
self.bezier_to(
orig_x - x_radius,
orig_y,
orig_x - x_c,
orig_y - y_radius,
orig_x - x_radius,
orig_y - y_c,
)?;
self.close_path()
}
pub fn close_path(&mut self) -> Result<(), PdfiumError> {
if self
.bindings()
.is_true(unsafe { self.bindings().FPDFPath_Close(self.object_handle) })
{
Ok(())
} else {
Err(PdfiumError::PdfiumLibraryInternalError(
PdfiumInternalError::Unknown,
))
}
}
pub fn fill_mode(&self) -> Result<PdfPathFillMode, PdfiumError> {
let mut raw_fill_mode: c_int = 0;
let mut _raw_stroke: FPDF_BOOL = self.bindings().FALSE();
if self.bindings().is_true(unsafe {
self.bindings().FPDFPath_GetDrawMode(
self.object_handle(),
&mut raw_fill_mode,
&mut _raw_stroke,
)
}) {
PdfPathFillMode::from_pdfium(raw_fill_mode)
} else {
Err(PdfiumError::PdfiumLibraryInternalError(
PdfiumInternalError::Unknown,
))
}
}
pub fn is_stroked(&self) -> Result<bool, PdfiumError> {
let mut _raw_fill_mode: c_int = 0;
let mut raw_stroke: FPDF_BOOL = self.bindings().FALSE();
if self.bindings().is_true(unsafe {
self.bindings().FPDFPath_GetDrawMode(
self.object_handle(),
&mut _raw_fill_mode,
&mut raw_stroke,
)
}) {
Ok(self.bindings().is_true(raw_stroke))
} else {
Err(PdfiumError::PdfiumLibraryInternalError(
PdfiumInternalError::Unknown,
))
}
}
pub fn set_fill_and_stroke_mode(
&mut self,
fill_mode: PdfPathFillMode,
do_stroke: bool,
) -> Result<(), PdfiumError> {
if self.bindings().is_true(unsafe {
self.bindings().FPDFPath_SetDrawMode(
self.object_handle(),
fill_mode.as_pdfium() as c_int,
self.bindings().bool_to_pdfium(do_stroke),
)
}) {
Ok(())
} else {
Err(PdfiumError::PdfiumLibraryInternalError(
PdfiumInternalError::Unknown,
))
}
}
#[inline]
pub fn segments(&self) -> PdfPagePathObjectSegments<'_> {
PdfPagePathObjectSegments::from_pdfium(self.object_handle())
}
create_transform_setters!(
&mut Self,
Result<(), PdfiumError>,
"this [PdfPagePathObject]",
"this [PdfPagePathObject].",
"this [PdfPagePathObject],"
);
create_transform_getters!(
"this [PdfPagePathObject]",
"this [PdfPagePathObject].",
"this [PdfPagePathObject],"
);
}
impl<'a> PdfPageObjectPrivate<'a> for PdfPagePathObject<'a> {
#[inline]
fn object_handle(&self) -> FPDF_PAGEOBJECT {
self.object_handle
}
#[inline]
fn ownership(&self) -> &PdfPageObjectOwnership {
&self.ownership
}
#[inline]
fn set_ownership(&mut self, ownership: PdfPageObjectOwnership) {
self.ownership = ownership;
}
}
impl<'a> Drop for PdfPagePathObject<'a> {
fn drop(&mut self) {
self.drop_impl();
}
}
impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfPagePathObject<'a> {}
#[cfg(feature = "thread_safe")]
unsafe impl<'a> Send for PdfPagePathObject<'a> {}
#[cfg(feature = "thread_safe")]
unsafe impl<'a> Sync for PdfPagePathObject<'a> {}
pub struct PdfPagePathObjectSegments<'a> {
handle: FPDF_PAGEOBJECT,
matrix: Option<PdfMatrix>,
lifetime: PhantomData<&'a FPDF_PAGEOBJECT>,
}
impl<'a> PdfPagePathObjectSegments<'a> {
#[inline]
pub(crate) fn from_pdfium(handle: FPDF_PAGEOBJECT) -> Self {
Self {
handle,
matrix: None,
lifetime: PhantomData,
}
}
#[inline]
pub fn transform(&self, matrix: PdfMatrix) -> PdfPagePathObjectSegments<'a> {
Self {
handle: self.handle,
matrix: Some(matrix),
lifetime: PhantomData,
}
}
#[inline]
pub fn raw(&self) -> PdfPagePathObjectSegments<'a> {
Self {
handle: self.handle,
matrix: None,
lifetime: PhantomData,
}
}
}
impl<'a> PdfPathSegments<'a> for PdfPagePathObjectSegments<'a> {
#[inline]
fn len(&self) -> PdfPathSegmentIndex {
unsafe {
self.bindings()
.FPDFPath_CountSegments(self.handle)
.try_into()
.unwrap_or(0)
}
}
fn get(&self, index: PdfPathSegmentIndex) -> Result<PdfPathSegment<'a>, PdfiumError> {
let handle = unsafe {
self.bindings()
.FPDFPath_GetPathSegment(self.handle, index as c_int)
};
if handle.is_null() {
Err(PdfiumError::PdfiumLibraryInternalError(
PdfiumInternalError::Unknown,
))
} else {
Ok(PdfPathSegment::from_pdfium(handle, self.matrix))
}
}
#[inline]
fn iter(&'a self) -> PdfPathSegmentsIterator<'a> {
PdfPathSegmentsIterator::new(self)
}
}
impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfPagePathObjectSegments<'a> {}
#[cfg(feature = "thread_safe")]
unsafe impl<'a> Send for PdfPagePathObjectSegments<'a> {}
#[cfg(feature = "thread_safe")]
unsafe impl<'a> Sync for PdfPagePathObjectSegments<'a> {}