@@ -59,10 +59,11 @@ impl HtmlHandlebars {
5959
6060 let content = utils:: render_markdown ( & ch. content , ctx. html_config . smart_punctuation ( ) ) ;
6161
62- let fixed_content = utils:: render_markdown_with_path (
62+ let printed_item = utils:: render_markdown_with_path_and_redirects (
6363 & ch. content ,
6464 ctx. html_config . smart_punctuation ( ) ,
6565 Some ( path) ,
66+ & ctx. html_config . redirect ,
6667 ) ;
6768 if !ctx. is_index && ctx. html_config . print . page_break {
6869 // Add page break between chapters
@@ -71,7 +72,25 @@ impl HtmlHandlebars {
7172 print_content
7273 . push_str ( r#"<div style="break-before: page; page-break-before: always;"></div>"# ) ;
7374 }
74- print_content. push_str ( & fixed_content) ;
75+ let print_page_id = {
76+ let mut base = path. display ( ) . to_string ( ) ;
77+ if base. ends_with ( ".md" ) {
78+ base. truncate ( base. len ( ) - 3 ) ;
79+ }
80+ & base
81+ . replace ( "/" , "-" )
82+ . replace ( "\\ " , "-" )
83+ . to_ascii_lowercase ( )
84+ } ;
85+
86+ // We have to build header links in advance so that we can know the ranges
87+ // for the headers in one page.
88+ // Insert a dummy div to make sure that we can locate the specific page.
89+ print_content. push_str ( & ( format ! ( r#"<div id="{print_page_id}"></div>"# ) ) ) ;
90+ print_content. push_str ( & build_header_links (
91+ & build_print_element_id ( & printed_item, & print_page_id) ,
92+ Some ( print_page_id) ,
93+ ) ) ;
7594
7695 // Update the context with data for this file
7796 let ctx_path = path
@@ -216,7 +235,23 @@ impl HtmlHandlebars {
216235 code_config : & Code ,
217236 edition : Option < RustEdition > ,
218237 ) -> String {
219- let rendered = build_header_links ( & rendered) ;
238+ let rendered = build_header_links ( & rendered, None ) ;
239+ let rendered = self . post_process_common ( rendered, & playground_config, code_config, edition) ;
240+
241+ rendered
242+ }
243+
244+ /// Applies some post-processing to the HTML to apply some adjustments.
245+ ///
246+ /// This common function is used for both normal chapters (via
247+ /// `post_process`) and the combined print page.
248+ fn post_process_common (
249+ & self ,
250+ rendered : String ,
251+ playground_config : & Playground ,
252+ code_config : & Code ,
253+ edition : Option < RustEdition > ,
254+ ) -> String {
220255 let rendered = fix_code_blocks ( & rendered) ;
221256 let rendered = add_playground_pre ( & rendered, playground_config, edition) ;
222257 let rendered = hide_lines ( & rendered, code_config) ;
@@ -465,7 +500,7 @@ impl Renderer for HtmlHandlebars {
465500 debug ! ( "Render template" ) ;
466501 let rendered = handlebars. render ( "index" , & data) ?;
467502
468- let rendered = self . post_process (
503+ let rendered = self . post_process_common (
469504 rendered,
470505 & html_config. playground ,
471506 & html_config. code ,
@@ -661,9 +696,35 @@ fn make_data(
661696 Ok ( data)
662697}
663698
699+ /// Go through the rendered print page HTML,
700+ /// add path id prefix to all the elements id as well as footnote links.
701+ fn build_print_element_id ( html : & str , print_page_id : & str ) -> String {
702+ static ALL_ID : LazyLock < Regex > =
703+ LazyLock :: new ( || Regex :: new ( r#"(<[^>]*?id=")([^"]+?)""# ) . unwrap ( ) ) ;
704+ static FOOTNOTE_ID : LazyLock < Regex > = LazyLock :: new ( || {
705+ Regex :: new (
706+ r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""## ,
707+ )
708+ . unwrap ( )
709+ } ) ;
710+
711+ let temp_html = ALL_ID . replace_all ( html, |caps : & Captures < ' _ > | {
712+ format ! ( "{}{}-{}\" " , & caps[ 1 ] , print_page_id, & caps[ 2 ] )
713+ } ) ;
714+
715+ FOOTNOTE_ID
716+ . replace_all ( & temp_html, |caps : & Captures < ' _ > | {
717+ format ! ( "{}{}-{}\" " , & caps[ 1 ] , print_page_id, & caps[ 2 ] )
718+ } )
719+ . into_owned ( )
720+ }
721+
664722/// Goes through the rendered HTML, making sure all header tags have
665723/// an anchor respectively so people can link to sections directly.
666- fn build_header_links ( html : & str ) -> String {
724+ ///
725+ /// `print_page_id` should be set to the print page ID prefix when adjusting the
726+ /// print page.
727+ fn build_header_links ( html : & str , print_page_id : Option < & str > ) -> String {
667728 static BUILD_HEADER_LINKS : LazyLock < Regex > = LazyLock :: new ( || {
668729 Regex :: new ( r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"# ) . unwrap ( )
669730 } ) ;
@@ -692,21 +753,34 @@ fn build_header_links(html: &str) -> String {
692753 caps. get ( 2 ) . map ( |x| x. as_str ( ) . to_string ( ) ) ,
693754 caps. get ( 3 ) . map ( |x| x. as_str ( ) . to_string ( ) ) ,
694755 & mut id_counter,
756+ print_page_id,
695757 )
696758 } )
697759 . into_owned ( )
698760}
699761
700762/// Insert a single link into a header, making sure each link gets its own
701763/// unique ID by appending an auto-incremented number (if necessary).
764+ ///
765+ /// For `print.html`, we will add a path id prefix.
702766fn insert_link_into_header (
703767 level : usize ,
704768 content : & str ,
705769 id : Option < String > ,
706770 classes : Option < String > ,
707771 id_counter : & mut HashMap < String , usize > ,
772+ print_page_id : Option < & str > ,
708773) -> String {
709- let id = id. unwrap_or_else ( || utils:: unique_id_from_content ( content, id_counter) ) ;
774+ let id = if let Some ( print_page_id) = print_page_id {
775+ let content_id = {
776+ #[ allow( deprecated) ]
777+ utils:: id_from_content ( content)
778+ } ;
779+ let with_prefix = format ! ( "{} {}" , print_page_id, content_id) ;
780+ id. unwrap_or_else ( || utils:: unique_id_from_content ( & with_prefix, id_counter) )
781+ } else {
782+ id. unwrap_or_else ( || utils:: unique_id_from_content ( content, id_counter) )
783+ } ;
710784 let classes = classes
711785 . map ( |s| format ! ( " class=\" {s}\" " ) )
712786 . unwrap_or_default ( ) ;
@@ -986,7 +1060,7 @@ mod tests {
9861060 ] ;
9871061
9881062 for ( src, should_be) in inputs {
989- let got = build_header_links ( src) ;
1063+ let got = build_header_links ( src, None ) ;
9901064 assert_eq ! ( got, should_be) ;
9911065 }
9921066 }
0 commit comments