Skip to content

Commit 58ce528

Browse files
andiwandclaude
andcommitted
fix(html): put a page-anchored frame on the page, not in the text
A frame took its placement from its wrap alone, so one anchored to the page floated into the running text: a letter's address, date and footer boxes landed between the paragraphs instead of in their fields. Only a frame anchored in the text reads the wrap now; one anchored to the page sits at its own coordinates on it. The reference output also picks up the shipped css and js, which had drifted from what the engine emits since #679, #682, #683 and #685 - the same html rendered differently there than freshly generated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J8PCMZUVxuGstmmgQzEdif
1 parent 5a07030 commit 58ce528

2 files changed

Lines changed: 13 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ The release run heads these entries with the version and opens a fresh
4040
- A pdf page is the size of its crop box and shows what is on it and no more,
4141
as a viewer shows it.
4242
- A pdf's JPEG 2000 images render. New dependency: `openjpeg`.
43+
- A frame anchored to the page sits where the page says, whatever the text
44+
around it wraps like - a letter's address and date boxes land in their fields
45+
instead of in the running text.
4346

4447
## v6.5.0 - 2026-08-10
4548

src/odr/internal/html/document_style.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -422,13 +422,18 @@ std::string html::translate_frame_properties(const Frame &frame) {
422422
horizontal_position = *style.horizontal_position;
423423
}
424424

425+
// A frame anchored to the page sits at its own place on it, whatever the text
426+
// around it does; only one anchored in the text takes its place from the
427+
// wrap.
428+
const AnchorType anchor_type = frame.anchor_type();
429+
const bool in_text = anchor_type != AnchorType::at_page;
430+
425431
// The frame is what its image sizes against.
426432
std::string result;
427-
if (const AnchorType anchor_type = frame.anchor_type();
428-
anchor_type == AnchorType::as_char) {
433+
if (anchor_type == AnchorType::as_char) {
429434
result += "position:relative;";
430435
result += "display:inline-block;";
431-
} else if (text_wrap == TextWrap::before) {
436+
} else if (in_text && text_wrap == TextWrap::before) {
432437
result += "position:relative;";
433438
result += "display:block;";
434439
result += "float:right;clear:both;";
@@ -446,7 +451,7 @@ std::string html::translate_frame_properties(const Frame &frame) {
446451
result += width->to_string();
447452
result += ");";
448453
}
449-
} else if (text_wrap == TextWrap::after) {
454+
} else if (in_text && text_wrap == TextWrap::after) {
450455
result += "position:relative;";
451456
result += "display:block;";
452457
result += "float:left;clear:both;";
@@ -457,7 +462,7 @@ std::string html::translate_frame_properties(const Frame &frame) {
457462
if (const std::optional<Measure> y = frame.y(); y.has_value()) {
458463
result += "margin-top:" + y->to_string() + ";";
459464
}
460-
} else if (text_wrap == TextWrap::none) {
465+
} else if (in_text && text_wrap == TextWrap::none) {
461466
result += "position:relative;";
462467
result += "display:block;";
463468
if (const std::optional<Measure> x = frame.x(); x.has_value()) {

0 commit comments

Comments
 (0)