use super::*;
use crate::layout::elements::Container;
pub(in crate::render::pdf) fn render_container(
content: &mut String,
element: &Container,
frame: PageElementFrame<'_>,
phase: ElementPaintPhase,
ctx: &mut PageRenderContext<'_>,
) {
let page_size = frame.page_size;
let margin = frame.margin;
let available_width = frame.available_width;
let y_pos = &frame.y_pos;
let children = &element.children;
let background_color = &element.paint.background.color;
let border = &element.box_model.border;
let c_border_radii = &element.paint.border_radii;
let c_outline_width = &element.paint.outline.width;
let c_padding = &element.box_model.padding;
let block_width = &element.box_model.size.width;
let c_block_height = &element.box_model.size.height;
let c_bg_blend = &element.paint.background.blend_mode;
let c_visible = &element.paint.visible;
let c_float = &element.flow.float;
let c_position = &element.positioning.scheme;
let c_offset_left = &element.positioning.insets.left;
let c_overflow = &element.overflow.combined;
let c_overflow_x = &element.overflow.x;
let c_overflow_y = &element.overflow.y;
let c_box_transform = &element.paint.group.transform;
let c_transform = &c_box_transform.value;
let c_containing_block = &element.positioning.containing_block;
let c_box_shadow = &element.paint.shadows;
let c_bg_gradient = &element.paint.background.layers.gradient;
let c_bg_radial = &element.paint.background.layers.radial_gradient;
let c_bg_conic = &element.paint.background.layers.conic_gradient;
let c_bg_svg = &element.paint.background.layers.svg;
let c_bg_blur = &element.paint.background.layers.blur_radius;
let c_bg_clip = &element.paint.background.layers.clip;
let c_positioned_depth = &element.positioning.containing_block_depth;
let c_visible_self = *c_visible;
let container_w = block_width.resolve(available_width);
let container_x = match c_position {
Position::Absolute | Position::Fixed => c_containing_block
.map_or(margin.left + c_offset_left, |cb| {
margin.left + cb.x + c_offset_left
}),
_ => match c_float {
Float::Right => margin.left + available_width - container_w,
_ => margin.left + c_offset_left,
},
};
let container_y_top = page_size.height - margin.top - y_pos;
let children_h: f32 = collapsed_children_height(children);
let content_h = c_padding.vertical() + children_h + border.vertical_width();
let total_h = c_block_height.resolve(content_h);
let c_geometry = LayoutBoxGeometry::from_layout(
PdfRect::new(container_x, container_y_top - total_h, container_w, total_h),
border,
*c_padding,
element.paint.border_image.as_ref(),
);
let page_content = ctx.text.pdf_writer.page_content_transform;
let c_box_geometry = ctx.text.pdf_writer.resolve_box_geometry(c_geometry);
let c_paint_geometry = c_box_geometry.painting();
let c_fragment_geometry = c_box_geometry.fragment(element.fragmentation);
let c_paint_box = c_paint_geometry.border_box;
if c_visible_self
&& let Some(t) = c_transform
&& is_projected_transform(t)
&& projected_solid_children_are_empty(children)
&& c_bg_gradient.is_none()
&& c_bg_radial.is_none()
&& c_bg_conic.is_none()
&& c_bg_svg.is_none()
&& *c_bg_blur == 0.0
&& c_border_radii.is_zero()
&& *c_outline_width == 0.0
&& c_box_shadow.is_empty()
&& element.paint.group.effects.is_identity()
&& *c_bg_blend == crate::style::computed::BlendMode::Normal
&& *c_bg_clip == BackgroundClip::Border
&& !c_overflow.clips()
{
render_projected_solid_box(
content,
page_content,
c_box_transform,
c_paint_geometry,
*background_color,
border,
);
return;
}
if c_visible_self
&& let Some(t) = c_transform
&& !is_projected_transform(t)
&& projected_solid_children_are_empty(children)
&& c_bg_gradient.is_none()
&& c_bg_radial.is_none()
&& c_bg_conic.is_none()
&& c_bg_svg.is_none()
&& *c_bg_blur == 0.0
&& c_border_radii.is_zero()
&& *c_outline_width == 0.0
&& c_box_shadow.is_empty()
&& element.paint.group.effects.is_identity()
&& *c_bg_blend == crate::style::computed::BlendMode::Normal
&& *c_bg_clip == BackgroundClip::Border
&& !c_overflow.clips()
&& render_affine_solid_box(
content,
ctx.text.pdf_writer,
ctx.text.page_images,
c_box_transform,
c_paint_geometry,
*background_color,
border,
)
{
return;
}
let c_group = PaintGroupScope::begin(content, element, c_fragment_geometry, ctx);
if c_visible_self
&& *c_bg_blur > 0.0
&& !children.is_empty()
&& c_bg_gradient.is_none()
&& c_bg_radial.is_none()
&& c_bg_conic.is_none()
&& c_bg_svg.is_none()
&& c_border_radii.is_zero()
&& *c_outline_width == 0.0
&& !border.has_visible()
&& let Some(blurred) = blurred_simple_container_group(
children,
c_paint_box.width,
c_paint_box.height,
*background_color,
border,
*c_padding,
*c_bg_blur,
ctx.text.pdf_writer.opts.raster_quality.filter_dpi,
ctx.text.custom_fonts,
)
{
let img_obj_id = ctx.text.pdf_writer.add_image_object(
&blurred.asset.data,
blurred.asset.source_width,
blurred.asset.source_height,
blurred.asset.format,
blurred.asset.png_metadata.as_ref(),
);
let img_name = format!("Im{img_obj_id}");
let ov = blurred.overflow_pt;
content.push_str(&format!(
"q\n{w} 0 0 {h} {ix} {iy} cm\n/{name} Do\nQ\n",
w = c_paint_box.width + 2.0 * ov,
h = c_paint_box.height + 2.0 * ov,
ix = c_paint_box.left - ov,
iy = c_paint_box.bottom - ov,
name = img_name,
));
ctx.text.page_images.push(ImageRef {
name: img_name,
obj_id: img_obj_id,
});
c_group.finish(content, ctx);
return;
}
if c_visible_self
&& *c_bg_blur > 0.0
&& children.is_empty()
&& c_bg_gradient.is_none()
&& c_bg_radial.is_none()
&& c_bg_conic.is_none()
&& c_bg_svg.is_none()
&& c_border_radii.is_zero()
&& *c_outline_width == 0.0
&& let Some(blurred) = crate::render::blur::blur_box(
c_paint_box.width,
c_paint_box.height,
*background_color,
border,
*c_bg_blur,
ctx.text.pdf_writer.opts.raster_quality.filter_dpi,
)
{
let img_obj_id = ctx.text.pdf_writer.add_image_object(
&blurred.asset.data,
blurred.asset.source_width,
blurred.asset.source_height,
blurred.asset.format,
blurred.asset.png_metadata.as_ref(),
);
let img_name = format!("Im{img_obj_id}");
let ov = blurred.overflow_pt;
content.push_str(&format!(
"q\n{w} 0 0 {h} {ix} {iy} cm\n/{name} Do\nQ\n",
w = c_paint_box.width + 2.0 * ov,
h = c_paint_box.height + 2.0 * ov,
ix = c_paint_box.left - ov,
iy = c_paint_box.bottom - ov,
name = img_name,
));
ctx.text.page_images.push(ImageRef {
name: img_name,
obj_id: img_obj_id,
});
c_group.finish(content, ctx);
return;
}
if phase.paints_decoration() {
container_decoration::paint_container_decoration(
content,
element,
frame,
c_fragment_geometry,
ctx,
);
}
let c_padding_box = c_geometry.padding_box();
let c_paint_padding_box = c_paint_geometry.padding_box();
let c_pad_box_w = c_padding_box.width;
let c_pad_box_h = c_padding_box.height;
let c_avail_w = (c_pad_box_w - c_padding.horizontal()).max(0.0);
let c_avail_h = (c_pad_box_h - c_padding.vertical()).max(0.0);
let (c_over_w, c_over_h) = children_overflow_extent(children);
let c_ratio_h = if c_avail_w > 0.0 {
c_over_w / c_avail_w
} else {
0.0
};
let c_ratio_v = if c_avail_h > 0.0 {
c_over_h / c_avail_h
} else {
0.0
};
let c_scroll_ok = c_border_radii.is_zero();
let c_has_v = c_scroll_ok
&& match c_overflow_y {
Overflow::Scroll => true,
Overflow::Auto => c_ratio_v > 1.001,
_ => false,
};
let c_has_h = c_scroll_ok
&& match c_overflow_x {
Overflow::Scroll => true,
Overflow::Auto => c_ratio_h > 1.001,
_ => false,
};
let c_sb = SCROLLBAR_THICKNESS_PT;
let c_v_gutter = if c_has_v { c_sb } else { 0.0 };
let c_h_gutter = if c_has_h { c_sb } else { 0.0 };
let c_scrollport_w = (c_avail_w - c_v_gutter).max(0.0);
let c_scrollport_h = (c_avail_h - c_h_gutter).max(0.0);
let c_thumb_ratio_h = if c_scrollport_w > 0.0 {
c_over_w / c_scrollport_w
} else {
c_ratio_h
};
let c_thumb_ratio_v = if c_scrollport_h > 0.0 {
c_over_h / c_scrollport_h
} else {
c_ratio_v
};
let needs_clip = c_overflow.clips();
let clip_command = needs_clip.then(|| {
let mut command = String::from("q\n");
if c_has_v || c_has_h {
let cx = c_paint_padding_box.left;
let cy = c_paint_padding_box.bottom + c_h_gutter;
let cw = c_paint_padding_box.width - c_v_gutter;
let ch = c_paint_padding_box.height - c_h_gutter;
command.push_str(&format!("{cx} {cy} {cw} {ch} re W n\n"));
} else {
command.push_str(&overflow_clip_path(
c_paint_box.left,
c_paint_box.bottom,
c_paint_box.width,
c_paint_box.height,
c_paint_geometry.border,
*c_border_radii,
));
command.push_str("W n\n");
}
command
});
if let Some(command) = &clip_command {
content.push_str(command);
ctx.stacking.push_clip(command.clone());
}
let c_content_box = c_geometry.content_box();
let inner_x = c_content_box.left;
let inner_w = c_content_box.width;
let inner_y = c_content_box.top();
let mut abs_origins: HashMap<usize, PdfPoint> = HashMap::new();
if *c_positioned_depth > 0 && (c_position.is_positioned() || c_transform.is_some()) {
abs_origins.insert(
*c_positioned_depth,
PdfPoint::new(c_padding_box.left, c_padding_box.top()),
);
}
if phase.paints_contents() {
render_container_children(
content,
children,
ContainerFrame::new(
PdfPoint::new(inner_x, inner_y),
crate::types::Size::new(inner_w, c_content_box.height),
PdfPoint::new(c_padding_box.left, c_padding_box.top()),
),
&mut abs_origins,
ctx,
ContainerRenderOptions {
device_space_available: c_transform.is_none(),
paint_phase: ElementPaintPhase::All,
stacking_scope: StackingScope::for_element(element),
},
);
}
if needs_clip {
ctx.stacking.pop_clip();
content.push_str("Q\n");
}
if phase.paints_decoration() && (c_has_v || c_has_h) {
paint_scrollbars(
content,
c_paint_padding_box.left,
c_paint_padding_box.bottom,
c_paint_padding_box.width,
c_paint_padding_box.height,
c_has_v,
c_has_h,
c_thumb_ratio_v.max(1.0),
c_thumb_ratio_h.max(1.0),
);
}
c_group.finish(content, ctx);
}