fop_layout/layout/engine/
types.rs1use fop_core::{tree::RetrievePosition, NodeId};
7use fop_types::Length;
8use std::collections::HashMap;
9
10#[derive(Debug, Clone)]
12pub(super) struct MarkerEntry {
13 pub(super) node_id: NodeId,
15 pub(super) starts_on_page: bool,
17 pub(super) ends_on_page: bool,
19}
20
21#[derive(Debug, Default)]
23pub(super) struct MarkerMap {
24 pub(super) markers: HashMap<String, Vec<MarkerEntry>>,
27}
28
29impl MarkerMap {
30 pub(super) fn new() -> Self {
32 Self {
33 markers: HashMap::new(),
34 }
35 }
36
37 #[allow(dead_code)]
39 pub(super) fn add_marker(
40 &mut self,
41 class_name: String,
42 node_id: NodeId,
43 starts_on_page: bool,
44 ends_on_page: bool,
45 ) {
46 let entry = MarkerEntry {
47 node_id,
48 starts_on_page,
49 ends_on_page,
50 };
51
52 self.markers.entry(class_name).or_default().push(entry);
53 }
54
55 pub(super) fn retrieve_marker(
57 &self,
58 class_name: &str,
59 position: RetrievePosition,
60 ) -> Option<NodeId> {
61 let entries = self.markers.get(class_name)?;
62
63 match position {
64 RetrievePosition::FirstStartingWithinPage => {
65 entries.iter().find(|e| e.starts_on_page).map(|e| e.node_id)
67 }
68 RetrievePosition::FirstIncludingCarryover => {
69 entries.first().map(|e| e.node_id)
71 }
72 RetrievePosition::LastStartingWithinPage => {
73 entries
75 .iter()
76 .rev()
77 .find(|e| e.starts_on_page)
78 .map(|e| e.node_id)
79 }
80 RetrievePosition::LastEndingWithinPage => {
81 entries
83 .iter()
84 .rev()
85 .find(|e| e.ends_on_page)
86 .map(|e| e.node_id)
87 }
88 }
89 }
90
91 pub(super) fn clear(&mut self) {
93 self.markers.clear();
94 }
95}
96
97#[derive(Debug, Clone, Copy)]
103pub(super) struct PageRegionGeometry {
104 pub page_width: Length,
106 pub page_height: Length,
108 pub before_rect: fop_types::Rect,
110 pub after_rect: fop_types::Rect,
112 pub start_rect: fop_types::Rect,
114 pub end_rect: fop_types::Rect,
116 pub body_rect: fop_types::Rect,
118}
119
120#[derive(Debug, Clone)]
125pub struct MultiColumnLayout {
126 pub column_count: i32,
128 pub column_gap: Length,
130 pub available_width: Length,
132 pub column_width: Length,
134 pub current_column: i32,
136 pub column_y: Length,
138 pub max_column_height: Option<Length>,
140}
141
142impl MultiColumnLayout {
143 pub fn new(column_count: i32, column_gap: Length, available_width: Length) -> Self {
145 let total_gap = column_gap * (column_count - 1);
147 let column_width = (available_width - total_gap) / column_count;
148
149 Self {
150 column_count,
151 column_gap,
152 available_width,
153 column_width,
154 current_column: 0,
155 column_y: Length::ZERO,
156 max_column_height: None,
157 }
158 }
159
160 pub fn with_max_height(mut self, max_height: Length) -> Self {
162 self.max_column_height = Some(max_height);
163 self
164 }
165
166 pub fn current_column_x(&self) -> Length {
168 (self.column_width + self.column_gap) * self.current_column
169 }
170
171 pub fn is_column_filled(&self, content_height: Length) -> bool {
173 if let Some(max_height) = self.max_column_height {
174 self.column_y + content_height > max_height
175 } else {
176 false
177 }
178 }
179
180 pub fn next_column(&mut self) -> bool {
182 if self.current_column + 1 < self.column_count {
183 self.current_column += 1;
184 self.column_y = Length::ZERO;
185 true
186 } else {
187 false
189 }
190 }
191
192 pub fn allocate(&mut self, height: Length) -> (Length, Length) {
194 let x = self.current_column_x();
195 let y = self.column_y;
196 self.column_y += height;
197 (x, y)
198 }
199
200 pub fn reset(&mut self) {
202 self.current_column = 0;
203 self.column_y = Length::ZERO;
204 }
205
206 pub fn column_count(&self) -> i32 {
208 self.column_count
209 }
210
211 pub fn column_width(&self) -> Length {
213 self.column_width
214 }
215}
216
217#[derive(Debug, Clone, Copy, PartialEq, Eq)]
219pub enum FloatSide {
220 Left,
222 Right,
224 Start,
226 End,
228 Inside,
230 Outside,
232 None,
234}
235
236#[allow(dead_code)]
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
239pub enum ClearSide {
240 Left,
242 Right,
244 Both,
246 Start,
248 End,
250 None,
252}
253
254#[derive(Debug, Clone)]
256pub(super) struct FloatInfo {
257 #[allow(dead_code)]
259 pub(super) area_id: crate::area::AreaId,
260 pub(super) side: FloatSide,
262 pub(super) top: Length,
264 pub(super) bottom: Length,
266 pub(super) width: Length,
268}
269
270#[derive(Debug, Default)]
272pub(super) struct FloatManager {
273 pub(super) left_floats: Vec<FloatInfo>,
275 pub(super) right_floats: Vec<FloatInfo>,
277}
278
279impl FloatManager {
280 pub(super) fn new() -> Self {
282 Self {
283 left_floats: Vec::new(),
284 right_floats: Vec::new(),
285 }
286 }
287
288 pub(super) fn add_float(&mut self, float: FloatInfo, is_odd_page: bool) {
294 match float.side {
295 FloatSide::Left | FloatSide::Start => {
296 self.left_floats.push(float);
297 }
298 FloatSide::Right | FloatSide::End => {
299 self.right_floats.push(float);
300 }
301 FloatSide::Inside => {
302 if is_odd_page {
305 self.left_floats.push(float);
307 } else {
308 self.right_floats.push(float);
310 }
311 }
312 FloatSide::Outside => {
313 if is_odd_page {
315 self.right_floats.push(float);
317 } else {
318 self.left_floats.push(float);
320 }
321 }
322 FloatSide::None => {}
323 }
324 }
325
326 pub(super) fn available_width(&self, y: Length, container_width: Length) -> (Length, Length) {
328 let left_offset = self.get_left_offset(y);
329 let right_offset = self.get_right_offset(y);
330 let available = container_width - left_offset - right_offset;
331 (left_offset, available)
332 }
333
334 pub(super) fn get_left_offset(&self, y: Length) -> Length {
336 self.left_floats
337 .iter()
338 .filter(|f| f.top <= y && y < f.bottom)
339 .map(|f| f.width)
340 .fold(Length::ZERO, |acc, w| acc + w)
341 }
342
343 pub(super) fn get_right_offset(&self, y: Length) -> Length {
345 self.right_floats
346 .iter()
347 .filter(|f| f.top <= y && y < f.bottom)
348 .map(|f| f.width)
349 .fold(Length::ZERO, |acc, w| acc + w)
350 }
351
352 #[allow(dead_code)]
354 pub(super) fn get_clear_position(&self, clear: ClearSide, current_y: Length) -> Length {
355 match clear {
356 ClearSide::Left | ClearSide::Start => self
357 .left_floats
358 .iter()
359 .filter(|f| f.bottom > current_y)
360 .map(|f| f.bottom)
361 .max()
362 .unwrap_or(current_y),
363 ClearSide::Right | ClearSide::End => self
364 .right_floats
365 .iter()
366 .filter(|f| f.bottom > current_y)
367 .map(|f| f.bottom)
368 .max()
369 .unwrap_or(current_y),
370 ClearSide::Both => {
371 let left_bottom = self
372 .left_floats
373 .iter()
374 .filter(|f| f.bottom > current_y)
375 .map(|f| f.bottom)
376 .max()
377 .unwrap_or(current_y);
378 let right_bottom = self
379 .right_floats
380 .iter()
381 .filter(|f| f.bottom > current_y)
382 .map(|f| f.bottom)
383 .max()
384 .unwrap_or(current_y);
385 left_bottom.max(right_bottom)
386 }
387 ClearSide::None => current_y,
388 }
389 }
390
391 pub(super) fn remove_floats_above(&mut self, y: Length) {
393 self.left_floats.retain(|f| f.bottom > y);
394 self.right_floats.retain(|f| f.bottom > y);
395 }
396
397 pub(super) fn clear(&mut self) {
399 self.left_floats.clear();
400 self.right_floats.clear();
401 }
402}
403
404#[derive(Debug, Clone)]
406#[allow(dead_code)]
407pub(super) struct PageContext {
408 pub(super) page_number: usize,
410 pub(super) total_pages: Option<usize>,
412 pub(super) is_first: bool,
414 pub(super) is_last: bool,
416}
417
418impl PageContext {
419 #[allow(dead_code)]
421 pub(super) fn new() -> Self {
422 Self {
423 page_number: 1,
424 total_pages: None,
425 is_first: true,
426 is_last: false,
427 }
428 }
429
430 #[allow(dead_code)]
432 pub(super) fn is_odd_page(&self) -> bool {
433 self.page_number % 2 == 1
434 }
435
436 #[allow(dead_code)]
438 pub(super) fn is_even_page(&self) -> bool {
439 self.page_number.is_multiple_of(2)
440 }
441
442 #[allow(dead_code)]
444 pub(super) fn is_first_page(&self) -> bool {
445 self.is_first
446 }
447
448 #[allow(dead_code)]
450 pub(super) fn is_last_page(&self) -> bool {
451 self.is_last
452 }
453}
454
455#[allow(dead_code)]
457pub(super) fn parse_fo_length(s: &str) -> Option<Length> {
458 if let Some(v) = s.strip_suffix("pt") {
459 v.parse::<f64>().ok().map(Length::from_pt)
460 } else if let Some(v) = s.strip_suffix("mm") {
461 v.parse::<f64>().ok().map(Length::from_mm)
462 } else if let Some(v) = s.strip_suffix("cm") {
463 v.parse::<f64>().ok().map(Length::from_cm)
464 } else if let Some(v) = s.strip_suffix("in") {
465 v.parse::<f64>().ok().map(Length::from_inch)
466 } else if let Some(v) = s.strip_suffix("px") {
467 v.parse::<f64>().ok().map(|px| Length::from_pt(px * 0.75))
468 } else {
469 None
470 }
471}