Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ The release run heads these entries with the version and opens a fresh
- An ods formula with spaces between a function name and its parenthesis, as
LibreOffice writes `ROUND (`, parses, computes and moves with structural
edits.
- A text page that holds a frame anchored to the page, such as a letter
template, prints whole on one sheet, so the frame stays where the page puts
it.

## v7.5.0 - 2026-10-06

Expand Down
12 changes: 11 additions & 1 deletion src/odr/internal/html/document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,16 @@ viewport_mode_override(const Document &document, const HtmlConfig &config) {

/// The sheet a page box prints on, sized by the first page. A text page puts
/// its margins on the sheet instead, so text that runs past a sheet keeps them.
/// A frame placed on the page stands off the page edge, not the margin, so a
/// text page holding one prints whole like a slide.
void write_print_page_style(HtmlWriter &out, const Document &document) {
const Element root = document.root_element();
const bool text = document.document_type() == DocumentType::text;
const bool fixed =
!text || std::ranges::any_of(root.children(), [](const Element &child) {
return child.type() == ElementType::frame &&
child.as_frame().anchor_type() == AnchorType::at_page;
});

PageLayout page_layout;
if (text) {
Expand All @@ -178,8 +185,11 @@ void write_print_page_style(HtmlWriter &out, const Document &document) {
out.out() << "size:" << css_length(*page_layout.width) << " "
<< css_length(*page_layout.height) << ";";
}
if (!text) {
if (fixed) {
out.out() << "margin:0}";
if (text) {
out.out() << "@media print{.odr-page-outer{min-height:0!important}}";
}
out.write_header_style_end();
return;
}
Expand Down
36 changes: 36 additions & 0 deletions test/src/html_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,42 @@ TEST(html, a_text_page_prints_with_its_margins_on_the_sheet) {
EXPECT_EQ(render_odt(config).find("@page{"), std::string::npos);
}

TEST(html, a_text_page_with_a_frame_on_the_page_prints_whole) {
const auto render = [](const std::string &anchor) {
const std::string fodt =
R"(<?xml version="1.0" encoding="UTF-8"?>)"
R"(<office:document)"
R"( xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0")"
R"( xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0")"
R"( xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0")"
R"( xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0")"
R"( office:version="1.3")"
R"( office:mimetype="application/vnd.oasis.opendocument.text">)"
R"(<office:body><office:text><draw:frame text:anchor-type=")" +
anchor +
R"(" svg:x="1cm" svg:y="1cm" svg:width="2cm" svg:height="1cm">)"
R"(<draw:text-box><text:p>head</text:p></draw:text-box></draw:frame>)"
R"(<text:p>body</text:p></office:text></office:body></office:document>)";
HtmlConfig config;
config.text_document_margin = true;
std::ostringstream out;
html::translate(open(File::from_memory(fodt),
DecodeOptions::as(FileType::opendocument_text)),
config)
.list_views()
.at(0)
.write_html(out);
return std::move(out).str();
};

const std::string page = render("page");
EXPECT_NE(page.find("@page{margin:0}"), std::string::npos);
EXPECT_EQ(page.find(".odr-page-inner{margin:0!important}"),
std::string::npos);
EXPECT_NE(render("paragraph").find(".odr-page-inner{margin:0!important}"),
std::string::npos);
}

// A browser floors the page scale at 0.25, so a page more than four screens
// wide cannot be zoomed out to. A wider page states a floor of its own.
TEST(html, fitting_the_width_states_a_floor_a_wide_page_needs) {
Expand Down
Loading