pub(super) mod block;
pub(super) mod convert;
pub(super) mod table;
use std::collections::HashMap;
use crate::model::{self, Block};
use crate::render::dimension::Pt;
use crate::render::layout::fragment::Fragment;
use crate::render::layout::measurer::TextMeasurer;
use crate::render::layout::page::PageConfig;
use crate::render::layout::paragraph::ParagraphStyle;
use crate::render::layout::section::LayoutBlock;
use crate::render::resolve::images::MediaEntry;
use crate::render::resolve::sections::ResolvedSection;
use crate::render::resolve::ResolvedDocument;
use block::{
build_block, build_fragments, collect_endnotes, extract_floating_images,
find_vml_absolute_position,
};
use convert::{
doc_font_family, doc_font_size, paragraph_style_from_props, resolve_paragraph_defaults,
};
use table::build_table;
pub(super) const SPEC_FALLBACK_FONT: &str = "Times New Roman";
pub(super) const SPEC_DEFAULT_FONT_SIZE: Pt = Pt::new(10.0);
pub struct BuildContext<'a> {
pub measurer: &'a TextMeasurer,
pub resolved: &'a ResolvedDocument,
}
impl BuildContext<'_> {
pub(super) fn media(&self) -> &HashMap<model::RelId, MediaEntry> {
&self.resolved.media
}
}
#[derive(Default)]
pub struct BuildState {
pub page_config: crate::render::layout::page::PageConfig,
pub footnote_counter: u32,
pub endnote_counter: u32,
pub list_counters: HashMap<(model::NumId, u8), u32>,
pub field_ctx: crate::render::layout::fragment::FieldContext,
}
pub struct BuiltSection {
pub blocks: Vec<LayoutBlock>,
pub endnotes: Vec<(String, Vec<Fragment>, ParagraphStyle)>,
}
pub fn build_section_blocks(
section: &ResolvedSection,
config: &PageConfig,
ctx: &BuildContext,
state: &mut BuildState,
) -> BuiltSection {
let mut pending_dropcap: Option<crate::render::layout::paragraph::DropCapInfo> = None;
let blocks: Vec<LayoutBlock> = section
.blocks
.iter()
.filter_map(|block| {
build_block(
block,
config.content_width(),
ctx,
state,
&mut pending_dropcap,
)
})
.collect();
let mut endnotes = Vec::new();
collect_endnotes(ctx, state, &mut endnotes);
BuiltSection { blocks, endnotes }
}
pub struct HeaderFooterContent {
pub blocks: Vec<LayoutBlock>,
pub absolute_position: Option<(Pt, Pt)>,
pub floating_images: Vec<crate::render::layout::section::FloatingImage>,
}
pub fn build_header_footer_content(
blocks: &[Block],
ctx: &BuildContext,
state: &mut BuildState,
) -> HeaderFooterContent {
let mut layout_blocks = Vec::new();
let mut all_floating_images = Vec::new();
let mut absolute_position: Option<(Pt, Pt)> = None;
let available_width = state.page_config.content_width();
let block_count = blocks.len();
for (block_i, block) in blocks.iter().enumerate() {
match block {
Block::Paragraph(p) => {
let (mut frags, props) = build_fragments(p, ctx, state, None, None);
let style = paragraph_style_from_props(&props);
if absolute_position.is_none() {
for inline in &p.content {
if let Some(pos) = find_vml_absolute_position(inline) {
absolute_position = Some(pos);
break;
}
}
}
let floats = extract_floating_images(p, ctx, state, false);
all_floating_images.extend(floats);
if frags.is_empty() && block_i + 1 < block_count {
let (family, mut size, ..) = resolve_paragraph_defaults(p, ctx.resolved, false);
if let Some(ref mrp) = p.mark_run_properties {
if let Some(fs) = mrp.font_size {
size = Pt::from(fs);
}
}
let line_height = ctx.measurer.default_line_height(&family, size);
frags.push(Fragment::LineBreak { line_height });
}
layout_blocks.push(LayoutBlock::Paragraph {
fragments: frags,
style,
page_break_before: false,
footnotes: vec![],
floating_images: vec![], });
}
Block::Table(t) => {
let built = build_table(t, available_width, ctx, state);
layout_blocks.push(LayoutBlock::Table {
rows: built.rows,
col_widths: built.col_widths,
border_config: built.border_config,
indent: built.indent,
alignment: built.alignment,
float_info: built.float_info,
style_id: t.properties.style_id.clone(),
});
}
Block::SectionBreak(_) => {}
}
}
HeaderFooterContent {
blocks: layout_blocks,
absolute_position,
floating_images: all_floating_images,
}
}
pub fn default_line_height(ctx: &BuildContext) -> Pt {
let family = doc_font_family(ctx);
let size = doc_font_size(ctx);
ctx.measurer.default_line_height(&family, size)
}