@@ -67,7 +67,25 @@ impl HtmlHandlebars {
6767 print_content
6868 . push_str ( r#"<div style="break-before: page; page-break-before: always;"></div>"# ) ;
6969 }
70- print_content. push_str ( & fixed_content) ;
70+ let print_page_id = {
71+ let mut base = path. display ( ) . to_string ( ) ;
72+ if base. ends_with ( ".md" ) {
73+ base. truncate ( base. len ( ) - 3 ) ;
74+ }
75+ & base
76+ . replace ( "/" , "-" )
77+ . replace ( "\\ " , "-" )
78+ . to_ascii_lowercase ( )
79+ } ;
80+
81+ // We have to build header links in advance so that we can know the ranges
82+ // for the headers in one page.
83+ // Insert a dummy div to make sure that we can locate the specific page.
84+ print_content. push_str ( & ( format ! ( r#"<div id="{print_page_id}"></div>"# ) ) ) ;
85+ print_content. push_str ( & build_header_links (
86+ & build_print_element_id ( & fixed_content, & print_page_id) ,
87+ Some ( print_page_id) ,
88+ ) ) ;
7189
7290 // Update the context with data for this file
7391 let ctx_path = path
@@ -239,7 +257,23 @@ impl HtmlHandlebars {
239257 code_config : & Code ,
240258 edition : Option < RustEdition > ,
241259 ) -> String {
242- let rendered = build_header_links ( & rendered) ;
260+ let rendered = build_header_links ( & rendered, None ) ;
261+ let rendered = self . post_process_common ( rendered, & playground_config, code_config, edition) ;
262+
263+ rendered
264+ }
265+
266+ /// Applies some post-processing to the HTML to apply some adjustments.
267+ ///
268+ /// This common function is used for both normal chapters (via
269+ /// `post_process`) and the combined print page.
270+ fn post_process_common (
271+ & self ,
272+ rendered : String ,
273+ playground_config : & Playground ,
274+ code_config : & Code ,
275+ edition : Option < RustEdition > ,
276+ ) -> String {
243277 let rendered = fix_code_blocks ( & rendered) ;
244278 let rendered = add_playground_pre ( & rendered, playground_config, edition) ;
245279 let rendered = hide_lines ( & rendered, code_config) ;
@@ -497,7 +531,7 @@ impl Renderer for HtmlHandlebars {
497531 debug ! ( "Render template" ) ;
498532 let rendered = handlebars. render ( "index" , & data) ?;
499533
500- let rendered = self . post_process (
534+ let rendered = self . post_process_common (
501535 rendered,
502536 & html_config. playground ,
503537 & html_config. code ,
@@ -698,9 +732,32 @@ fn make_data(
698732 Ok ( data)
699733}
700734
735+ /// Go through the rendered print page HTML,
736+ /// add path id prefix to all the elements id as well as footnote links.
737+ fn build_print_element_id ( html : & str , print_page_id : & str ) -> String {
738+ static_regex ! ( ALL_ID , r#"(<[^>]*?id=")([^"]+?)""# ) ;
739+ static_regex ! (
740+ FOOTNOTE_ID ,
741+ r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""##
742+ ) ;
743+
744+ let temp_html = ALL_ID . replace_all ( html, |caps : & Captures < ' _ > | {
745+ format ! ( "{}{}-{}\" " , & caps[ 1 ] , print_page_id, & caps[ 2 ] )
746+ } ) ;
747+
748+ FOOTNOTE_ID
749+ . replace_all ( & temp_html, |caps : & Captures < ' _ > | {
750+ format ! ( "{}{}-{}\" " , & caps[ 1 ] , print_page_id, & caps[ 2 ] )
751+ } )
752+ . into_owned ( )
753+ }
754+
701755/// Goes through the rendered HTML, making sure all header tags have
702756/// an anchor respectively so people can link to sections directly.
703- fn build_header_links ( html : & str ) -> String {
757+ ///
758+ /// `print_page_id` should be set to the print page ID prefix when adjusting the
759+ /// print page.
760+ fn build_header_links ( html : & str , print_page_id : Option < & str > ) -> String {
704761 static_regex ! (
705762 BUILD_HEADER_LINKS ,
706763 r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"#
@@ -730,21 +787,34 @@ fn build_header_links(html: &str) -> String {
730787 caps. get ( 2 ) . map ( |x| x. as_str ( ) . to_string ( ) ) ,
731788 caps. get ( 3 ) . map ( |x| x. as_str ( ) . to_string ( ) ) ,
732789 & mut id_counter,
790+ print_page_id,
733791 )
734792 } )
735793 . into_owned ( )
736794}
737795
738796/// Insert a single link into a header, making sure each link gets its own
739797/// unique ID by appending an auto-incremented number (if necessary).
798+ ///
799+ /// For `print.html`, we will add a path id prefix.
740800fn insert_link_into_header (
741801 level : usize ,
742802 content : & str ,
743803 id : Option < String > ,
744804 classes : Option < String > ,
745805 id_counter : & mut HashMap < String , usize > ,
806+ print_page_id : Option < & str > ,
746807) -> String {
747- let id = id. unwrap_or_else ( || utils:: unique_id_from_content ( content, id_counter) ) ;
808+ let id = if let Some ( print_page_id) = print_page_id {
809+ let content_id = {
810+ #[ allow( deprecated) ]
811+ utils:: id_from_content ( content)
812+ } ;
813+ let with_prefix = format ! ( "{} {}" , print_page_id, content_id) ;
814+ id. unwrap_or_else ( || utils:: unique_id_from_content ( & with_prefix, id_counter) )
815+ } else {
816+ id. unwrap_or_else ( || utils:: unique_id_from_content ( content, id_counter) )
817+ } ;
748818 let classes = classes
749819 . map ( |s| format ! ( " class=\" {s}\" " ) )
750820 . unwrap_or_default ( ) ;
@@ -1125,7 +1195,7 @@ mod tests {
11251195 ] ;
11261196
11271197 for ( src, should_be) in inputs {
1128- let got = build_header_links ( src) ;
1198+ let got = build_header_links ( src, None ) ;
11291199 assert_eq ! ( got, should_be) ;
11301200 }
11311201 }
0 commit comments