use std::io::Write;
use dxpdf::render::layout::draw_command::{DrawCommand, LayoutedPage};
fn make_docx(document_xml: &str) -> Vec<u8> {
let buf = std::io::Cursor::new(Vec::new());
let mut zip = zip::ZipWriter::new(buf);
let o = zip::write::SimpleFileOptions::default()
.compression_method(zip::CompressionMethod::Deflated);
zip.start_file("[Content_Types].xml", o).unwrap();
zip.write_all(
br#"<?xml version="1.0" encoding="UTF-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml"
ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
</Types>"#,
)
.unwrap();
zip.start_file("_rels/.rels", o).unwrap();
zip.write_all(
br#"<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
Target="word/document.xml"/>
</Relationships>"#,
)
.unwrap();
zip.start_file("word/document.xml", o).unwrap();
zip.write_all(document_xml.as_bytes()).unwrap();
zip.finish().unwrap().into_inner()
}
fn table(tbl_w: &str, grid: &str) -> Vec<u8> {
let cells: String = grid
.matches("<w:gridCol")
.enumerate()
.map(|(i, _)| format!(r#"<w:tc><w:p><w:r><w:t>c{i}</w:t></w:r></w:p></w:tc>"#))
.collect();
make_docx(&format!(
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:tbl>
<w:tblPr>
{tbl_w}
<w:tblBorders>
<w:top w:val="single" w:sz="4" w:color="000000"/>
<w:left w:val="single" w:sz="4" w:color="000000"/>
<w:bottom w:val="single" w:sz="4" w:color="000000"/>
<w:right w:val="single" w:sz="4" w:color="000000"/>
<w:insideH w:val="single" w:sz="4" w:color="000000"/>
<w:insideV w:val="single" w:sz="4" w:color="000000"/>
</w:tblBorders>
</w:tblPr>
<w:tblGrid>{grid}</w:tblGrid>
<w:tr>{cells}</w:tr>
</w:tbl>
</w:body>
</w:document>"#
))
}
fn layout(bytes: &[u8]) -> Vec<LayoutedPage> {
let parsed = dxpdf::docx::parse(bytes).expect("parse");
dxpdf::render::resolve_and_layout(parsed).1
}
const OUTER: f32 = 0.5;
fn drawn_right_edge(pages: &[LayoutedPage]) -> f32 {
pages
.iter()
.flat_map(|p| &p.commands)
.filter_map(|c| match c {
DrawCommand::Rect { rect, .. } => Some((rect.origin.x + rect.size.width).raw()),
_ => None,
})
.fold(f32::NEG_INFINITY, f32::max)
}
#[test]
fn an_auto_table_wider_than_the_page_is_kept_on_the_page() {
let pages = layout(&table(
r#"<w:tblW w:w="0" w:type="auto"/>"#,
r#"<w:gridCol w:w="20000"/>"#,
));
let page_width = pages[0].page_size.width.raw();
assert!(
drawn_right_edge(&pages) <= page_width + OUTER + 0.01,
"table drawn to x={:.1} on a {page_width:.0} pt page — {:.1} pt of it is \
off the paper, more than the {OUTER} pt its own border may hang",
drawn_right_edge(&pages),
drawn_right_edge(&pages) - page_width,
);
}
#[test]
fn an_auto_table_that_overflows_the_margin_but_not_the_paper_is_untouched() {
let pages = layout(&table(
r#"<w:tblW w:w="0" w:type="auto"/>"#,
r#"<w:gridCol w:w="10000"/>"#,
));
assert!(
(drawn_right_edge(&pages) - 572.0).abs() < 0.01,
"declared grid was rescaled: right edge {:.2}, expected {:.2}",
drawn_right_edge(&pages),
572.0,
);
}
#[test]
fn an_auto_table_that_fits_keeps_its_declared_grid() {
let pages = layout(&table(
r#"<w:tblW w:w="0" w:type="auto"/>"#,
r#"<w:gridCol w:w="3000"/><w:gridCol w:w="3000"/>"#,
));
assert!(
(drawn_right_edge(&pages) - 372.0).abs() < 0.01,
"a fitting grid was rescaled: right edge {:.2}, expected {:.2}",
drawn_right_edge(&pages),
372.0,
);
}
#[test]
fn a_declared_width_wider_than_the_page_is_left_to_its_own_scaling() {
let pages = layout(&table(
r#"<w:tblW w:w="20000" w:type="dxa"/>"#,
r#"<w:gridCol w:w="20000"/>"#,
));
assert!(
drawn_right_edge(&pages) > pages[0].page_size.width.raw(),
"the dxa path was clamped too: right edge {:.2}",
drawn_right_edge(&pages),
);
}