@@ -59,10 +59,11 @@ impl HtmlHandlebars {
59
59
60
60
let content = utils:: render_markdown ( & ch. content , ctx. html_config . smart_punctuation ( ) ) ;
61
61
62
- let fixed_content = utils:: render_markdown_with_path (
62
+ let printed_item = utils:: render_markdown_with_path_and_redirects (
63
63
& ch. content ,
64
64
ctx. html_config . smart_punctuation ( ) ,
65
65
Some ( path) ,
66
+ & ctx. html_config . redirect ,
66
67
) ;
67
68
if !ctx. is_index && ctx. html_config . print . page_break {
68
69
// Add page break between chapters
@@ -71,7 +72,25 @@ impl HtmlHandlebars {
71
72
print_content
72
73
. push_str ( r#"<div style="break-before: page; page-break-before: always;"></div>"# ) ;
73
74
}
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
+ ) ) ;
75
94
76
95
// Update the context with data for this file
77
96
let ctx_path = path
@@ -224,7 +243,23 @@ impl HtmlHandlebars {
224
243
code_config : & Code ,
225
244
edition : Option < RustEdition > ,
226
245
) -> String {
227
- let rendered = build_header_links ( & rendered) ;
246
+ let rendered = build_header_links ( & rendered, None ) ;
247
+ let rendered = self . post_process_common ( rendered, & playground_config, code_config, edition) ;
248
+
249
+ rendered
250
+ }
251
+
252
+ /// Applies some post-processing to the HTML to apply some adjustments.
253
+ ///
254
+ /// This common function is used for both normal chapters (via
255
+ /// `post_process`) and the combined print page.
256
+ fn post_process_common (
257
+ & self ,
258
+ rendered : String ,
259
+ playground_config : & Playground ,
260
+ code_config : & Code ,
261
+ edition : Option < RustEdition > ,
262
+ ) -> String {
228
263
let rendered = fix_code_blocks ( & rendered) ;
229
264
let rendered = add_playground_pre ( & rendered, playground_config, edition) ;
230
265
let rendered = hide_lines ( & rendered, code_config) ;
@@ -479,7 +514,7 @@ impl Renderer for HtmlHandlebars {
479
514
debug ! ( "Render template" ) ;
480
515
let rendered = handlebars. render ( "index" , & data) ?;
481
516
482
- let rendered = self . post_process (
517
+ let rendered = self . post_process_common (
483
518
rendered,
484
519
& html_config. playground ,
485
520
& html_config. code ,
@@ -675,9 +710,35 @@ fn make_data(
675
710
Ok ( data)
676
711
}
677
712
713
+ /// Go through the rendered print page HTML,
714
+ /// add path id prefix to all the elements id as well as footnote links.
715
+ fn build_print_element_id ( html : & str , print_page_id : & str ) -> String {
716
+ static ALL_ID : LazyLock < Regex > =
717
+ LazyLock :: new ( || Regex :: new ( r#"(<[^>]*?id=")([^"]+?)""# ) . unwrap ( ) ) ;
718
+ static FOOTNOTE_ID : LazyLock < Regex > = LazyLock :: new ( || {
719
+ Regex :: new (
720
+ r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""## ,
721
+ )
722
+ . unwrap ( )
723
+ } ) ;
724
+
725
+ let temp_html = ALL_ID . replace_all ( html, |caps : & Captures < ' _ > | {
726
+ format ! ( "{}{}-{}\" " , & caps[ 1 ] , print_page_id, & caps[ 2 ] )
727
+ } ) ;
728
+
729
+ FOOTNOTE_ID
730
+ . replace_all ( & temp_html, |caps : & Captures < ' _ > | {
731
+ format ! ( "{}{}-{}\" " , & caps[ 1 ] , print_page_id, & caps[ 2 ] )
732
+ } )
733
+ . into_owned ( )
734
+ }
735
+
678
736
/// Goes through the rendered HTML, making sure all header tags have
679
737
/// an anchor respectively so people can link to sections directly.
680
- fn build_header_links ( html : & str ) -> String {
738
+ ///
739
+ /// `print_page_id` should be set to the print page ID prefix when adjusting the
740
+ /// print page.
741
+ fn build_header_links ( html : & str , print_page_id : Option < & str > ) -> String {
681
742
static BUILD_HEADER_LINKS : LazyLock < Regex > = LazyLock :: new ( || {
682
743
Regex :: new ( r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"# ) . unwrap ( )
683
744
} ) ;
@@ -706,21 +767,34 @@ fn build_header_links(html: &str) -> String {
706
767
caps. get ( 2 ) . map ( |x| x. as_str ( ) . to_string ( ) ) ,
707
768
caps. get ( 3 ) . map ( |x| x. as_str ( ) . to_string ( ) ) ,
708
769
& mut id_counter,
770
+ print_page_id,
709
771
)
710
772
} )
711
773
. into_owned ( )
712
774
}
713
775
714
776
/// Insert a single link into a header, making sure each link gets its own
715
777
/// unique ID by appending an auto-incremented number (if necessary).
778
+ ///
779
+ /// For `print.html`, we will add a path id prefix.
716
780
fn insert_link_into_header (
717
781
level : usize ,
718
782
content : & str ,
719
783
id : Option < String > ,
720
784
classes : Option < String > ,
721
785
id_counter : & mut HashMap < String , usize > ,
786
+ print_page_id : Option < & str > ,
722
787
) -> String {
723
- let id = id. unwrap_or_else ( || utils:: unique_id_from_content ( content, id_counter) ) ;
788
+ let id = if let Some ( print_page_id) = print_page_id {
789
+ let content_id = {
790
+ #[ allow( deprecated) ]
791
+ utils:: id_from_content ( content)
792
+ } ;
793
+ let with_prefix = format ! ( "{} {}" , print_page_id, content_id) ;
794
+ id. unwrap_or_else ( || utils:: unique_id_from_content ( & with_prefix, id_counter) )
795
+ } else {
796
+ id. unwrap_or_else ( || utils:: unique_id_from_content ( content, id_counter) )
797
+ } ;
724
798
let classes = classes
725
799
. map ( |s| format ! ( " class=\" {s}\" " ) )
726
800
. unwrap_or_default ( ) ;
@@ -1056,7 +1130,7 @@ mod tests {
1056
1130
] ;
1057
1131
1058
1132
for ( src, should_be) in inputs {
1059
- let got = build_header_links ( src) ;
1133
+ let got = build_header_links ( src, None ) ;
1060
1134
assert_eq ! ( got, should_be) ;
1061
1135
}
1062
1136
}
0 commit comments