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
//! Utilities for rectangle paths.
use crate::{
path::{PaintMode, WindingOrder},
Mm, Point, OP_PATH_CONST_RECT, OP_PATH_PAINT_END, OP_PATH_PAINT_STROKE,
};
/// A helper struct to insert rectangular shapes into a PDF.
///
/// This can be used to paint rectangles or to clip other paths.
#[derive(Debug, Copy, Clone)]
pub struct Rect {
/// Position of the lower left point of the rectangle, relative to the bottom left corner of
/// the PDF page in pt.
pub ll: Point,
/// Position of the upper right point of the rectangle, relative to the bottom left corner of
/// the PDF page in pt.
pub ur: Point,
/// The paint mode of the rectangle.
pub mode: PaintMode,
/// The path-painting/clipping path operator.
pub winding: WindingOrder,
}
impl Rect {
/// Create a new point.
///
/// **WARNING: The reference point for a point is the bottom left corner, not the top left**
#[inline]
pub fn new(llx: Mm, lly: Mm, urx: Mm, ury: Mm) -> Self {
Self {
ll: Point {
x: llx.into(),
y: lly.into(),
},
ur: Point {
x: urx.into(),
y: ury.into(),
},
mode: PaintMode::default(),
winding: WindingOrder::default(),
}
}
/// Returns a new `Rect` with the specified `mode`.
#[inline]
#[must_use]
pub fn with_mode(mut self, mode: PaintMode) -> Self {
self.mode = mode;
self
}
/// Returns a new `Rect` with the specified `winding`.
#[inline]
#[must_use]
pub fn with_winding(mut self, winding: WindingOrder) -> Self {
self.winding = winding;
self
}
/// Transform the `Rect` into a `Vec` of PDF [`Operation`]s.
///
/// [`Operation`]: lopdf::content::Operation
#[must_use]
pub fn into_stream_op(self) -> Vec<lopdf::content::Operation> {
use lopdf::content::Operation;
let width = self.ur.x - self.ll.x;
let height = self.ur.y - self.ll.y;
let rect_op = Operation::new(
OP_PATH_CONST_RECT,
vec![
self.ll.x.into(),
self.ll.y.into(),
width.into(),
height.into(),
],
);
let paint_op = match self.mode {
PaintMode::Clip => Operation::new(self.winding.get_clip_op(), vec![]),
PaintMode::Fill => Operation::new(self.winding.get_fill_op(), vec![]),
PaintMode::Stroke => Operation::new(OP_PATH_PAINT_STROKE, vec![]),
PaintMode::FillStroke => Operation::new(self.winding.get_fill_stroke_op(), vec![]),
};
if matches!(self.mode, PaintMode::Clip) {
vec![rect_op, paint_op, Operation::new(OP_PATH_PAINT_END, vec![])]
} else {
vec![rect_op, paint_op]
}
}
}
impl PartialEq for Rect {
// custom compare function because of floating point inaccuracy
fn eq(&self, other: &Rect) -> bool {
self.ll == other.ll && self.ur == other.ur
}
}