From 59343b525df32141bdd5c6ac36ec3137b9f79129 Mon Sep 17 00:00:00 2001
From: Eric Huss
Date: Thu, 30 Oct 2025 17:48:03 -0700
Subject: [PATCH 1/2] Add test for print relative for page that doesn't exist
An example where this can happen is a link to a page that was renamed
and redirected, or just a normal HTML file.
---
tests/testsuite/print/relative_links/expected/print.html | 4 ++++
tests/testsuite/print/relative_links/src/second/nested.md | 5 +++++
2 files changed, 9 insertions(+)
diff --git a/tests/testsuite/print/relative_links/expected/print.html b/tests/testsuite/print/relative_links/expected/print.html
index b1e181b7be..2dfbff2059 100644
--- a/tests/testsuite/print/relative_links/expected/print.html
+++ b/tests/testsuite/print/relative_links/expected/print.html
@@ -9,6 +9,10 @@ should work.
Link outside.
Link outside with anchor.
+Link inside but doesn’t exist.
+Link inside but doesn’t exist with anchor.
+Link inside to html.
+Link inside to html with anchor.

HTML Link
diff --git a/tests/testsuite/print/relative_links/src/second/nested.md b/tests/testsuite/print/relative_links/src/second/nested.md
index d888620391..443f00a22a 100644
--- a/tests/testsuite/print/relative_links/src/second/nested.md
+++ b/tests/testsuite/print/relative_links/src/second/nested.md
@@ -11,6 +11,11 @@ Link [outside](../../std/foo/bar.html).
Link [outside with anchor](../../std/foo/bar.html#panic).
+Link [inside but doesn't exist](../first/alpha/beta.md).
+Link [inside but doesn't exist with anchor](../first/alpha/beta.md#anchor).
+Link [inside to html](../first/alpha/gamma.html).
+Link [inside to html with anchor](../first/alpha/gamma.html#anchor).
+

HTML Link
From 005f4d648aedfa3e3d42b7c13ce0694d386f4b78 Mon Sep 17 00:00:00 2001
From: Eric Huss
Date: Thu, 30 Oct 2025 18:37:12 -0700
Subject: [PATCH 2/2] Fix print page links for internal links to non-chapters
This fixes links on the print page that go to an internal destination
that is not a chapter. The path would have the wrong relative
destination, and would be broken. The logic for detecting this was
incorrectly only checking if a link went outside the book, or didn't
have an html extension. This doesn't work for links to HTML files that
are inside the book, but not one of the chapters.
---
crates/mdbook-html/src/html/print.rs | 27 ++++++++++---------
.../print/relative_links/expected/print.html | 8 +++---
2 files changed, 18 insertions(+), 17 deletions(-)
diff --git a/crates/mdbook-html/src/html/print.rs b/crates/mdbook-html/src/html/print.rs
index 3688a979c3..e325396fd9 100644
--- a/crates/mdbook-html/src/html/print.rs
+++ b/crates/mdbook-html/src/html/print.rs
@@ -9,7 +9,7 @@ use crate::html::{ChapterTree, Element, serialize};
use crate::utils::{ToUrlPath, id_from_content, normalize_path, unique_id};
use mdbook_core::static_regex;
use std::collections::{HashMap, HashSet};
-use std::path::{Component, PathBuf};
+use std::path::PathBuf;
/// Takes all the chapter trees, modifies them to be suitable to render for
/// the print page, and returns an string of all the chapters rendered to a
@@ -166,13 +166,9 @@ fn rewrite_links(
{
lookup_key.pop();
lookup_key.push(href_path);
- let normalized = normalize_path(&lookup_key);
- // If this points outside of the book, don't modify it.
- let is_outside = matches!(
- normalized.components().next(),
- Some(Component::ParentDir | Component::RootDir)
- );
- if is_outside || !href_path.ends_with(".html") {
+ lookup_key = normalize_path(&lookup_key);
+ let is_a_chapter = path_to_root_id.contains_key(&lookup_key);
+ if !is_a_chapter {
// Make the link relative to the print page location.
let mut rel_path = normalize_path(&base.join(href_path)).to_url_path();
if let Some(anchor) = caps.name("anchor") {
@@ -184,10 +180,7 @@ fn rewrite_links(
}
}
- let lookup_key = normalize_path(&lookup_key);
-
- let anchor = caps.name("anchor");
- let id = match anchor {
+ let id = match caps.name("anchor") {
Some(anchor_id) => {
let anchor_id = anchor_id.as_str().to_string();
match id_remap.get(&lookup_key) {
@@ -204,7 +197,15 @@ fn rewrite_links(
}
None => match path_to_root_id.get(&lookup_key) {
Some(id) => id.to_string(),
- None => continue,
+ None => {
+ // This should be guaranteed that either the
+ // chapter itself is in the map (for anchor-only
+ // links), or the is_a_chapter check above.
+ panic!(
+ "internal error: expected `{lookup_key:?}` to be in \
+ root map (chapter path is `{html_path:?}`)"
+ );
+ }
},
};
el.insert_attr(attr, format!("#{id}").into());
diff --git a/tests/testsuite/print/relative_links/expected/print.html b/tests/testsuite/print/relative_links/expected/print.html
index 2dfbff2059..bff4ce1c65 100644
--- a/tests/testsuite/print/relative_links/expected/print.html
+++ b/tests/testsuite/print/relative_links/expected/print.html
@@ -9,10 +9,10 @@ should work.
Link outside.
Link outside with anchor.
-Link inside but doesn’t exist.
-Link inside but doesn’t exist with anchor.
-Link inside to html.
-Link inside to html with anchor.
+Link inside but doesn’t exist.
+Link inside but doesn’t exist with anchor.
+Link inside to html.
+Link inside to html with anchor.

HTML Link