@@ -68,7 +68,25 @@ impl HtmlHandlebars {
6868 print_content
6969 . push_str ( r#"<div style="break-before: page; page-break-before: always;"></div>"# ) ;
7070 }
71- print_content. push_str ( & fixed_content) ;
71+ let print_page_id = {
72+ let mut base = path. display ( ) . to_string ( ) ;
73+ if base. ends_with ( ".md" ) {
74+ base. truncate ( base. len ( ) - 3 ) ;
75+ }
76+ & base
77+ . replace ( "/" , "-" )
78+ . replace ( "\\ " , "-" )
79+ . to_ascii_lowercase ( )
80+ } ;
81+
82+ // We have to build header links in advance so that we can know the ranges
83+ // for the headers in one page.
84+ // Insert a dummy div to make sure that we can locate the specific page.
85+ print_content. push_str ( & ( format ! ( r#"<div id="{print_page_id}"></div>"# ) ) ) ;
86+ print_content. push_str ( & build_header_links (
87+ & build_print_element_id ( & fixed_content, & print_page_id) ,
88+ Some ( print_page_id) ,
89+ ) ) ;
7290
7391 // Update the context with data for this file
7492 let ctx_path = path
@@ -238,7 +256,23 @@ impl HtmlHandlebars {
238256 code_config : & Code ,
239257 edition : Option < RustEdition > ,
240258 ) -> String {
241- let rendered = build_header_links ( & rendered) ;
259+ let rendered = build_header_links ( & rendered, None ) ;
260+ let rendered = self . post_process_common ( rendered, & playground_config, code_config, edition) ;
261+
262+ rendered
263+ }
264+
265+ /// Applies some post-processing to the HTML to apply some adjustments.
266+ ///
267+ /// This common function is used for both normal chapters (via
268+ /// `post_process`) and the combined print page.
269+ fn post_process_common (
270+ & self ,
271+ rendered : String ,
272+ playground_config : & Playground ,
273+ code_config : & Code ,
274+ edition : Option < RustEdition > ,
275+ ) -> String {
242276 let rendered = fix_code_blocks ( & rendered) ;
243277 let rendered = add_playground_pre ( & rendered, playground_config, edition) ;
244278 let rendered = hide_lines ( & rendered, code_config) ;
@@ -490,7 +524,7 @@ impl Renderer for HtmlHandlebars {
490524 debug ! ( "Render template" ) ;
491525 let rendered = handlebars. render ( "index" , & data) ?;
492526
493- let rendered = self . post_process (
527+ let rendered = self . post_process_common (
494528 rendered,
495529 & html_config. playground ,
496530 & html_config. code ,
@@ -691,9 +725,32 @@ fn make_data(
691725 Ok ( data)
692726}
693727
728+ /// Go through the rendered print page HTML,
729+ /// add path id prefix to all the elements id as well as footnote links.
730+ fn build_print_element_id ( html : & str , print_page_id : & str ) -> String {
731+ static_regex ! ( ALL_ID , r#"(<[^>]*?id=")([^"]+?)""# ) ;
732+ static_regex ! (
733+ FOOTNOTE_ID ,
734+ r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""##
735+ ) ;
736+
737+ let temp_html = ALL_ID . replace_all ( html, |caps : & Captures < ' _ > | {
738+ format ! ( "{}{}-{}\" " , & caps[ 1 ] , print_page_id, & caps[ 2 ] )
739+ } ) ;
740+
741+ FOOTNOTE_ID
742+ . replace_all ( & temp_html, |caps : & Captures < ' _ > | {
743+ format ! ( "{}{}-{}\" " , & caps[ 1 ] , print_page_id, & caps[ 2 ] )
744+ } )
745+ . into_owned ( )
746+ }
747+
694748/// Goes through the rendered HTML, making sure all header tags have
695749/// an anchor respectively so people can link to sections directly.
696- fn build_header_links ( html : & str ) -> String {
750+ ///
751+ /// `print_page_id` should be set to the print page ID prefix when adjusting the
752+ /// print page.
753+ fn build_header_links ( html : & str , print_page_id : Option < & str > ) -> String {
697754 static_regex ! (
698755 BUILD_HEADER_LINKS ,
699756 r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"#
@@ -723,21 +780,34 @@ fn build_header_links(html: &str) -> String {
723780 caps. get ( 2 ) . map ( |x| x. as_str ( ) . to_string ( ) ) ,
724781 caps. get ( 3 ) . map ( |x| x. as_str ( ) . to_string ( ) ) ,
725782 & mut id_counter,
783+ print_page_id,
726784 )
727785 } )
728786 . into_owned ( )
729787}
730788
731789/// Insert a single link into a header, making sure each link gets its own
732790/// unique ID by appending an auto-incremented number (if necessary).
791+ ///
792+ /// For `print.html`, we will add a path id prefix.
733793fn insert_link_into_header (
734794 level : usize ,
735795 content : & str ,
736796 id : Option < String > ,
737797 classes : Option < String > ,
738798 id_counter : & mut HashMap < String , usize > ,
799+ print_page_id : Option < & str > ,
739800) -> String {
740- let id = id. unwrap_or_else ( || utils:: unique_id_from_content ( content, id_counter) ) ;
801+ let id = if let Some ( print_page_id) = print_page_id {
802+ let content_id = {
803+ #[ allow( deprecated) ]
804+ utils:: id_from_content ( content)
805+ } ;
806+ let with_prefix = format ! ( "{} {}" , print_page_id, content_id) ;
807+ id. unwrap_or_else ( || utils:: unique_id_from_content ( & with_prefix, id_counter) )
808+ } else {
809+ id. unwrap_or_else ( || utils:: unique_id_from_content ( content, id_counter) )
810+ } ;
741811 let classes = classes
742812 . map ( |s| format ! ( " class=\" {s}\" " ) )
743813 . unwrap_or_default ( ) ;
@@ -1126,7 +1196,7 @@ mod tests {
11261196 ] ;
11271197
11281198 for ( src, should_be) in inputs {
1129- let got = build_header_links ( src) ;
1199+ let got = build_header_links ( src, None ) ;
11301200 assert_eq ! ( got, should_be) ;
11311201 }
11321202 }
0 commit comments