laser_pdf/elements/
debug.rs1use utils::mm_to_pt;
2
3use crate::*;
4
5pub struct Debug<E: Element> {
6 pub element: E,
7 pub color: u8,
8 pub show_max_width: bool,
9 pub show_last_location_max_height: bool,
10}
11
12impl<E: Element> Debug<E> {
13 pub fn show_max_width(self) -> Self {
14 Self {
15 show_max_width: true,
16 ..self
17 }
18 }
19
20 pub fn show_last_location_max_height(self) -> Self {
21 Self {
22 show_last_location_max_height: true,
23 ..self
24 }
25 }
26}
27
28impl<E: Element> Element for Debug<E> {
29 fn first_location_usage(&self, ctx: FirstLocationUsageCtx) -> FirstLocationUsage {
30 self.element.first_location_usage(ctx)
31 }
32
33 fn measure(&self, ctx: MeasureCtx) -> ElementSize {
34 self.element.measure(ctx)
35 }
36
37 fn draw(&self, ctx: DrawCtx) -> ElementSize {
38 let size;
39 let mut last_location = ctx.location.clone();
40
41 let color = calculate_color(self.color);
42 let max_width = ctx.width.max;
43 let first_height = ctx.first_height;
44 let full_height = ctx.breakable.as_ref().map(|b| b.full_height);
45
46 let mut break_heights = Vec::new();
47
48 if let Some(breakable) = ctx.breakable {
49 size = self.element.draw(DrawCtx {
50 pdf: ctx.pdf,
51 breakable: Some(BreakableDraw {
52 do_break: &mut |pdf, location_idx, height| {
53 let break_count = break_heights.len() as u32;
54
55 if location_idx >= break_count {
56 break_heights.reserve((location_idx - break_count + 1) as usize);
57
58 break_heights.extend(
59 std::iter::repeat(None).take((location_idx - break_count) as usize),
60 );
61
62 break_heights.push(height);
63 last_location = (breakable.do_break)(pdf, location_idx, height);
64 last_location.clone()
65 } else {
66 let previous = break_heights[location_idx as usize];
67
68 assert_eq!(previous, height);
70
71 (breakable.do_break)(pdf, location_idx, height)
72 }
73 },
74 ..breakable
75 }),
76 location: ctx.location.clone(),
77 ..ctx
78 });
79
80 if size.width.is_some() || self.show_max_width {
81 for (i, &height) in break_heights.iter().enumerate() {
82 let full_height;
83 let location;
84
85 if i == 0 {
86 full_height = ctx.first_height;
87 location = ctx.location.clone();
88 } else {
89 full_height = breakable.full_height;
90 location =
91 (breakable.do_break)(ctx.pdf, i as u32 - 1, break_heights[i - 1]);
92 }
93
94 let dashed_size = (
95 if self.show_max_width {
96 max_width
97 } else {
98 size.width.unwrap()
99 },
100 full_height,
101 );
102
103 let dashed = match size.width.zip(height) {
104 Some(solid_size) => {
105 draw_box(ctx.pdf, location.clone(), solid_size, color, false);
106 solid_size != dashed_size
107 }
108 _ => true,
109 };
110
111 if dashed {
112 draw_box(ctx.pdf, location, dashed_size, color, dashed);
113 }
114 }
115 }
116 } else {
117 size = self.element.draw(DrawCtx {
118 pdf: ctx.pdf,
119 ..ctx
120 });
121 }
122
123 let dashed_size = (
124 if self.show_max_width {
125 Some(max_width)
126 } else {
127 size.width
128 },
129 if self.show_last_location_max_height {
130 Some(if break_heights.len() == 0 {
131 first_height
132 } else {
133 full_height.unwrap()
134 })
135 } else {
136 size.height
137 },
138 );
139
140 let dashed = if let (Some(width), Some(height)) = (size.width, size.height) {
141 draw_box(
142 ctx.pdf,
143 last_location.clone(),
144 (width, height),
145 color,
146 false,
147 );
148 dashed_size != (Some(width), Some(height))
149 } else {
150 true
151 };
152
153 if let Some((width, height)) = dashed.then_some(dashed_size.0.zip(dashed_size.1)).flatten()
154 {
155 draw_box(ctx.pdf, last_location, (width, height), color, true);
156 }
157
158 size
159 }
160}
161
162fn hue_to_rgb(hue: u8) -> [u8; 3] {
163 let x = 6u8.saturating_mul(43 - 43u8.abs_diff(hue % 85));
164
165 match hue / 43 {
166 0 => [255, x, 0],
167 1 => [x, 255, 0],
168 2 => [0, 255, x],
169 3 => [0, x, 255],
170 4 => [x, 0, 255],
171 5 => [255, 0, x],
172 _ => unreachable!(),
173 }
174}
175
176fn calculate_color(input: u8) -> [f32; 3] {
177 hue_to_rgb(input.reverse_bits()).map(|c| c as f32 / 255.)
178}
179
180fn draw_box(pdf: &mut Pdf, location: Location, size: (f32, f32), color: [f32; 3], dashed: bool) {
181 let layer = location.layer(pdf);
182
183 layer
184 .save_state()
185 .set_line_width(0.)
186 .set_stroke_rgb(color[0], color[1], color[2]);
187
188 if dashed {
189 layer.set_dash_pattern([2., 2.], 0.);
190 }
191
192 layer
193 .rect(
194 mm_to_pt(location.pos.0),
195 mm_to_pt(location.pos.1 - size.1),
196 mm_to_pt(size.0),
197 mm_to_pt(size.1),
198 )
199 .stroke()
200 .restore_state();
201}