use std::io::Write;
use dxpdf::render::layout::draw_command::{DrawCommand, LayoutedPage};
fn make_docx(document_xml: &str) -> Vec<u8> {
let mut buf = Vec::new();
{
let mut zip = zip::ZipWriter::new(std::io::Cursor::new(&mut 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();
}
buf
}
fn shape_document(anchor: &str) -> String {
format!(
r#"<?xml version="1.0" encoding="UTF-8"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape">
<w:body>
<w:p><w:r><w:drawing>
<wp:anchor distT="0" distB="0" distL="0" distR="0" simplePos="0"
relativeHeight="1" behindDoc="0" locked="0" layoutInCell="1" allowOverlap="1">
<wp:simplePos x="0" y="0"/>
<wp:positionH relativeFrom="margin"><wp:posOffset>0</wp:posOffset></wp:positionH>
<wp:positionV relativeFrom="paragraph"><wp:posOffset>0</wp:posOffset></wp:positionV>
<wp:extent cx="2540000" cy="1524000"/>
<wp:wrapNone/>
<wp:docPr id="1" name="Box"/>
<a:graphic><a:graphicData uri="http://schemas.microsoft.com/office/word/2010/wordprocessingShape">
<wps:wsp>
<wps:cNvSpPr/>
<wps:spPr>
<a:xfrm><a:off x="0" y="0"/><a:ext cx="2540000" cy="1524000"/></a:xfrm>
<a:prstGeom prst="rect"><a:avLst/></a:prstGeom>
</wps:spPr>
<wps:txbx><w:txbxContent>
<w:p><w:r><w:t>Boxed</w:t></w:r></w:p>
</w:txbxContent></wps:txbx>
<wps:bodyPr rot="0" vert="horz" wrap="square" vertOverflow="overflow"
lIns="50800" tIns="50800" rIns="50800" bIns="50800"
anchor="{anchor}"/>
</wps:wsp>
</a:graphicData></a:graphic>
</wp:anchor>
</w:drawing></w:r></w:p>
</w:body>
</w:document>"#
)
}
fn boxed_text_y(pages: &[LayoutedPage]) -> f32 {
pages
.iter()
.flat_map(|p| p.commands.iter())
.find_map(|c| match c {
DrawCommand::Text { text, position, .. } if &**text == "Boxed" => {
Some(position.y.raw())
}
_ => None,
})
.expect("the shape's text body is emitted")
}
fn y_for(anchor: &str) -> f32 {
let bytes = make_docx(&shape_document(anchor));
let doc = dxpdf::docx::parse(&bytes).expect("fixture parses");
let (_, pages) = dxpdf::render::resolve_and_layout(doc);
boxed_text_y(&pages)
}
#[test]
fn body_anchor_moves_shape_text_within_the_box() {
let (top, centre, bottom) = (y_for("t"), y_for("ctr"), y_for("b"));
assert!(
top < centre && centre < bottom,
"t < ctr < b, got {top} / {centre} / {bottom}"
);
let (half, full) = (centre - top, bottom - top);
assert!(
(full - 2.0 * half).abs() < 1e-3,
"the centre offset is half the bottom offset, got {half} / {full}"
);
let line_height = 112.0 - full;
assert!(
line_height > 0.0 && line_height < 112.0,
"one line fits the 112pt box, implied height {line_height}"
);
}
#[test]
fn an_omitted_anchor_is_top() {
let without = {
let xml = shape_document("t").replace("\n anchor=\"t\"", "");
assert!(!xml.contains("anchor="), "the attribute is gone: {xml}");
let bytes = make_docx(&xml);
let doc = dxpdf::docx::parse(&bytes).expect("fixture parses");
let (_, pages) = dxpdf::render::resolve_and_layout(doc);
boxed_text_y(&pages)
};
assert!(
(without - y_for("t")).abs() < 1e-3,
"no anchor attribute matches anchor=\"t\", got {without}"
);
}