use super::{LayoutCtx, LayoutResult, Layoutable};
use crate::geometry::{Constraints, Rect, Size};
use crate::render_node::RenderNode;
use crate::warnings::{LayoutWarning, LayoutWarningKind};
use lightweight_pdf_core::{Common, Element, TextStyle};
pub(crate) const EPS: f32 = 0.01;
pub(super) fn line_height_pt(style: &TextStyle) -> f32 {
style.size * style.line_height
}
pub(crate) fn push_warning(warnings: &mut Vec<LayoutWarning>, kind: LayoutWarningKind, page: usize, element_hint: impl Into<String>) {
warnings.push(LayoutWarning {
kind,
page,
element_hint: element_hint.into(),
});
}
pub(crate) fn measure_at_width(ctx: &LayoutCtx, child: &Element, width: f32) -> Size {
child.measure(
ctx,
Constraints {
max_width: width,
max_height: f32::INFINITY,
},
)
}
pub(crate) fn resolve_bound(explicit: Option<f32>, max: f32, padding: f32) -> (f32, f32) {
let bound = explicit.unwrap_or(max);
let inner = (bound - 2.0 * padding).max(0.0);
(bound, inner)
}
pub(crate) fn resolve_auto_size(explicit: Option<f32>, content: f32, padding: f32) -> f32 {
explicit.unwrap_or(content + 2.0 * padding)
}
fn resolve_bound_height(explicit: Option<f32>, padding: f32, default: f32) -> f32 {
explicit.map(|h| h - 2.0 * padding).unwrap_or(default)
}
pub(crate) fn shrink_and_bound_height(area: Rect, height: Option<f32>, padding: f32) -> (Rect, f32) {
let inner = area.shrink(padding);
let bound_height = resolve_bound_height(height, padding, inner.height);
(inner, bound_height)
}
pub(super) fn size_with_defaults(common: &Common, constraints: Constraints, default_height: f32) -> Size {
Size {
width: common.width.unwrap_or(constraints.max_width),
height: common.height.unwrap_or(default_height),
}
}
pub(crate) fn wrap_children(area: Rect, outer_height: f32, common: &Common, children: Vec<RenderNode>) -> RenderNode {
RenderNode::Group {
area: Rect {
height: outer_height,
..area
},
clip: true,
background: common.background,
border: common.border,
children,
}
}
pub(crate) fn finish_fit(common: &Common, area: Rect, content_extent: f32, children: Vec<RenderNode>) -> LayoutResult {
let outer_height = resolve_auto_size(common.height, content_extent, common.padding);
LayoutResult::Fit(wrap_children(area, outer_height, common, children))
}
pub(crate) fn coerce_to_fit(result: LayoutResult) -> RenderNode {
match result {
LayoutResult::Fit(node) => node,
LayoutResult::Split { current, .. } => current,
}
}
pub(crate) fn coerce_to_fit_and_warn(result: LayoutResult, warnings: &mut Vec<LayoutWarning>, page: usize, hint: &str) -> RenderNode {
if matches!(result, LayoutResult::Split { .. }) {
push_warning(warnings, LayoutWarningKind::ContentOverflow, page, hint);
}
coerce_to_fit(result)
}
pub(crate) fn clip_to_fixed_height(
area: Rect,
fixed_height: f32,
common: &Common,
rendered: Vec<RenderNode>,
warnings: &mut Vec<LayoutWarning>,
page: usize,
overflow_hint: Option<&str>,
) -> LayoutResult {
if let Some(hint) = overflow_hint {
push_warning(warnings, LayoutWarningKind::ContentOverflow, page, hint);
}
LayoutResult::Fit(wrap_children(area, fixed_height, common, rendered))
}