@@ -56,10 +56,11 @@ impl HtmlHandlebars {
5656
5757 let content = utils:: render_markdown ( & ch. content , ctx. html_config . smart_punctuation ( ) ) ;
5858
59- let fixed_content = utils:: render_markdown_with_path (
59+ let printed_item = utils:: render_markdown_with_path_and_redirects (
6060 & ch. content ,
6161 ctx. html_config . smart_punctuation ( ) ,
6262 Some ( path) ,
63+ & ctx. html_config . redirect ,
6364 ) ;
6465 if !ctx. is_index && ctx. html_config . print . page_break {
6566 // Add page break between chapters
@@ -68,7 +69,25 @@ impl HtmlHandlebars {
6869 print_content
6970 . push_str ( r#"<div style="break-before: page; page-break-before: always;"></div>"# ) ;
7071 }
71- print_content. push_str ( & fixed_content) ;
72+ let print_page_id = {
73+ let mut base = path. display ( ) . to_string ( ) ;
74+ if base. ends_with ( ".md" ) {
75+ base. truncate ( base. len ( ) - 3 ) ;
76+ }
77+ & base
78+ . replace ( "/" , "-" )
79+ . replace ( "\\ " , "-" )
80+ . to_ascii_lowercase ( )
81+ } ;
82+
83+ // We have to build header links in advance so that we can know the ranges
84+ // for the headers in one page.
85+ // Insert a dummy div to make sure that we can locate the specific page.
86+ print_content. push_str ( & ( format ! ( r#"<div id="{print_page_id}"></div>"# ) ) ) ;
87+ print_content. push_str ( & build_header_links (
88+ & build_print_element_id ( & printed_item, & print_page_id) ,
89+ Some ( print_page_id) ,
90+ ) ) ;
7291
7392 // Update the context with data for this file
7493 let ctx_path = path
@@ -214,7 +233,23 @@ impl HtmlHandlebars {
214233 code_config : & Code ,
215234 edition : Option < RustEdition > ,
216235 ) -> String {
217- let rendered = build_header_links ( & rendered) ;
236+ let rendered = build_header_links ( & rendered, None ) ;
237+ let rendered = self . post_process_common ( rendered, & playground_config, code_config, edition) ;
238+
239+ rendered
240+ }
241+
242+ /// Applies some post-processing to the HTML to apply some adjustments.
243+ ///
244+ /// This common function is used for both normal chapters (via
245+ /// `post_process`) and the combined print page.
246+ fn post_process_common (
247+ & self ,
248+ rendered : String ,
249+ playground_config : & Playground ,
250+ code_config : & Code ,
251+ edition : Option < RustEdition > ,
252+ ) -> String {
218253 let rendered = fix_code_blocks ( & rendered) ;
219254 let rendered = add_playground_pre ( & rendered, playground_config, edition) ;
220255 let rendered = hide_lines ( & rendered, code_config) ;
@@ -572,7 +607,7 @@ impl Renderer for HtmlHandlebars {
572607 debug ! ( "Render template" ) ;
573608 let rendered = handlebars. render ( "index" , & data) ?;
574609
575- let rendered = self . post_process (
610+ let rendered = self . post_process_common (
576611 rendered,
577612 & html_config. playground ,
578613 & html_config. code ,
@@ -783,9 +818,34 @@ fn make_data(
783818 Ok ( data)
784819}
785820
821+ /// Go through the rendered print page HTML,
822+ /// add path id prefix to all the elements id as well as footnote links.
823+ fn build_print_element_id ( html : & str , print_page_id : & str ) -> String {
824+ static ALL_ID : Lazy < Regex > = Lazy :: new ( || Regex :: new ( r#"(<[^>]*?id=")([^"]+?)""# ) . unwrap ( ) ) ;
825+ static FOOTNOTE_ID : Lazy < Regex > = Lazy :: new ( || {
826+ Regex :: new (
827+ r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""## ,
828+ )
829+ . unwrap ( )
830+ } ) ;
831+
832+ let temp_html = ALL_ID . replace_all ( html, |caps : & Captures < ' _ > | {
833+ format ! ( "{}{}-{}\" " , & caps[ 1 ] , print_page_id, & caps[ 2 ] )
834+ } ) ;
835+
836+ FOOTNOTE_ID
837+ . replace_all ( & temp_html, |caps : & Captures < ' _ > | {
838+ format ! ( "{}{}-{}\" " , & caps[ 1 ] , print_page_id, & caps[ 2 ] )
839+ } )
840+ . into_owned ( )
841+ }
842+
786843/// Goes through the rendered HTML, making sure all header tags have
787844/// an anchor respectively so people can link to sections directly.
788- fn build_header_links ( html : & str ) -> String {
845+ ///
846+ /// `print_page_id` should be set to the print page ID prefix when adjusting the
847+ /// print page.
848+ fn build_header_links ( html : & str , print_page_id : Option < & str > ) -> String {
789849 static BUILD_HEADER_LINKS : Lazy < Regex > = Lazy :: new ( || {
790850 Regex :: new ( r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"# ) . unwrap ( )
791851 } ) ;
@@ -814,21 +874,34 @@ fn build_header_links(html: &str) -> String {
814874 caps. get ( 2 ) . map ( |x| x. as_str ( ) . to_string ( ) ) ,
815875 caps. get ( 3 ) . map ( |x| x. as_str ( ) . to_string ( ) ) ,
816876 & mut id_counter,
877+ print_page_id,
817878 )
818879 } )
819880 . into_owned ( )
820881}
821882
822883/// Insert a sinle link into a header, making sure each link gets its own
823884/// unique ID by appending an auto-incremented number (if necessary).
885+ ///
886+ /// For `print.html`, we will add a path id prefix.
824887fn insert_link_into_header (
825888 level : usize ,
826889 content : & str ,
827890 id : Option < String > ,
828891 classes : Option < String > ,
829892 id_counter : & mut HashMap < String , usize > ,
893+ print_page_id : Option < & str > ,
830894) -> String {
831- let id = id. unwrap_or_else ( || utils:: unique_id_from_content ( content, id_counter) ) ;
895+ let id = if let Some ( print_page_id) = print_page_id {
896+ let content_id = {
897+ #[ allow( deprecated) ]
898+ utils:: id_from_content ( content)
899+ } ;
900+ let with_prefix = format ! ( "{} {}" , print_page_id, content_id) ;
901+ id. unwrap_or_else ( || utils:: unique_id_from_content ( & with_prefix, id_counter) )
902+ } else {
903+ id. unwrap_or_else ( || utils:: unique_id_from_content ( content, id_counter) )
904+ } ;
832905 let classes = classes
833906 . map ( |s| format ! ( " class=\" {s}\" " ) )
834907 . unwrap_or_default ( ) ;
@@ -1118,7 +1191,7 @@ mod tests {
11181191 ] ;
11191192
11201193 for ( src, should_be) in inputs {
1121- let got = build_header_links ( src) ;
1194+ let got = build_header_links ( src, None ) ;
11221195 assert_eq ! ( got, should_be) ;
11231196 }
11241197 }
0 commit comments