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
}
}