@@ -57,10 +57,11 @@ impl HtmlHandlebars {
57
57
58
58
let content = utils:: render_markdown ( & ch. content , ctx. html_config . smart_punctuation ( ) ) ;
59
59
60
- let fixed_content = utils:: render_markdown_with_path (
60
+ let printed_item = utils:: render_markdown_with_path_and_redirects (
61
61
& ch. content ,
62
62
ctx. html_config . smart_punctuation ( ) ,
63
63
Some ( path) ,
64
+ & ctx. html_config . redirect ,
64
65
) ;
65
66
if !ctx. is_index && ctx. html_config . print . page_break {
66
67
// Add page break between chapters
@@ -69,7 +70,25 @@ impl HtmlHandlebars {
69
70
print_content
70
71
. push_str ( r#"<div style="break-before: page; page-break-before: always;"></div>"# ) ;
71
72
}
72
- print_content. push_str ( & fixed_content) ;
73
+ let print_page_id = {
74
+ let mut base = path. display ( ) . to_string ( ) ;
75
+ if base. ends_with ( ".md" ) {
76
+ base. truncate ( base. len ( ) - 3 ) ;
77
+ }
78
+ & base
79
+ . replace ( "/" , "-" )
80
+ . replace ( "\\ " , "-" )
81
+ . to_ascii_lowercase ( )
82
+ } ;
83
+
84
+ // We have to build header links in advance so that we can know the ranges
85
+ // for the headers in one page.
86
+ // Insert a dummy div to make sure that we can locate the specific page.
87
+ print_content. push_str ( & ( format ! ( r#"<div id="{print_page_id}"></div>"# ) ) ) ;
88
+ print_content. push_str ( & build_header_links (
89
+ & build_print_element_id ( & printed_item, & print_page_id) ,
90
+ Some ( print_page_id) ,
91
+ ) ) ;
73
92
74
93
// Update the context with data for this file
75
94
let ctx_path = path
@@ -215,7 +234,23 @@ impl HtmlHandlebars {
215
234
code_config : & Code ,
216
235
edition : Option < RustEdition > ,
217
236
) -> String {
218
- let rendered = build_header_links ( & rendered) ;
237
+ let rendered = build_header_links ( & rendered, None ) ;
238
+ let rendered = self . post_process_common ( rendered, & playground_config, code_config, edition) ;
239
+
240
+ rendered
241
+ }
242
+
243
+ /// Applies some post-processing to the HTML to apply some adjustments.
244
+ ///
245
+ /// This common function is used for both normal chapters (via
246
+ /// `post_process`) and the combined print page.
247
+ fn post_process_common (
248
+ & self ,
249
+ rendered : String ,
250
+ playground_config : & Playground ,
251
+ code_config : & Code ,
252
+ edition : Option < RustEdition > ,
253
+ ) -> String {
219
254
let rendered = fix_code_blocks ( & rendered) ;
220
255
let rendered = add_playground_pre ( & rendered, playground_config, edition) ;
221
256
let rendered = hide_lines ( & rendered, code_config) ;
@@ -464,7 +499,7 @@ impl Renderer for HtmlHandlebars {
464
499
debug ! ( "Render template" ) ;
465
500
let rendered = handlebars. render ( "index" , & data) ?;
466
501
467
- let rendered = self . post_process (
502
+ let rendered = self . post_process_common (
468
503
rendered,
469
504
& html_config. playground ,
470
505
& html_config. code ,
@@ -660,9 +695,34 @@ fn make_data(
660
695
Ok ( data)
661
696
}
662
697
698
+ /// Go through the rendered print page HTML,
699
+ /// add path id prefix to all the elements id as well as footnote links.
700
+ fn build_print_element_id ( html : & str , print_page_id : & str ) -> String {
701
+ static ALL_ID : Lazy < Regex > = Lazy :: new ( || Regex :: new ( r#"(<[^>]*?id=")([^"]+?)""# ) . unwrap ( ) ) ;
702
+ static FOOTNOTE_ID : Lazy < Regex > = Lazy :: new ( || {
703
+ Regex :: new (
704
+ r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""## ,
705
+ )
706
+ . unwrap ( )
707
+ } ) ;
708
+
709
+ let temp_html = ALL_ID . replace_all ( html, |caps : & Captures < ' _ > | {
710
+ format ! ( "{}{}-{}\" " , & caps[ 1 ] , print_page_id, & caps[ 2 ] )
711
+ } ) ;
712
+
713
+ FOOTNOTE_ID
714
+ . replace_all ( & temp_html, |caps : & Captures < ' _ > | {
715
+ format ! ( "{}{}-{}\" " , & caps[ 1 ] , print_page_id, & caps[ 2 ] )
716
+ } )
717
+ . into_owned ( )
718
+ }
719
+
663
720
/// Goes through the rendered HTML, making sure all header tags have
664
721
/// an anchor respectively so people can link to sections directly.
665
- fn build_header_links ( html : & str ) -> String {
722
+ ///
723
+ /// `print_page_id` should be set to the print page ID prefix when adjusting the
724
+ /// print page.
725
+ fn build_header_links ( html : & str , print_page_id : Option < & str > ) -> String {
666
726
static BUILD_HEADER_LINKS : Lazy < Regex > = Lazy :: new ( || {
667
727
Regex :: new ( r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"# ) . unwrap ( )
668
728
} ) ;
@@ -691,21 +751,34 @@ fn build_header_links(html: &str) -> String {
691
751
caps. get ( 2 ) . map ( |x| x. as_str ( ) . to_string ( ) ) ,
692
752
caps. get ( 3 ) . map ( |x| x. as_str ( ) . to_string ( ) ) ,
693
753
& mut id_counter,
754
+ print_page_id,
694
755
)
695
756
} )
696
757
. into_owned ( )
697
758
}
698
759
699
760
/// Insert a sinle link into a header, making sure each link gets its own
700
761
/// unique ID by appending an auto-incremented number (if necessary).
762
+ ///
763
+ /// For `print.html`, we will add a path id prefix.
701
764
fn insert_link_into_header (
702
765
level : usize ,
703
766
content : & str ,
704
767
id : Option < String > ,
705
768
classes : Option < String > ,
706
769
id_counter : & mut HashMap < String , usize > ,
770
+ print_page_id : Option < & str > ,
707
771
) -> String {
708
- let id = id. unwrap_or_else ( || utils:: unique_id_from_content ( content, id_counter) ) ;
772
+ let id = if let Some ( print_page_id) = print_page_id {
773
+ let content_id = {
774
+ #[ allow( deprecated) ]
775
+ utils:: id_from_content ( content)
776
+ } ;
777
+ let with_prefix = format ! ( "{} {}" , print_page_id, content_id) ;
778
+ id. unwrap_or_else ( || utils:: unique_id_from_content ( & with_prefix, id_counter) )
779
+ } else {
780
+ id. unwrap_or_else ( || utils:: unique_id_from_content ( content, id_counter) )
781
+ } ;
709
782
let classes = classes
710
783
. map ( |s| format ! ( " class=\" {s}\" " ) )
711
784
. unwrap_or_default ( ) ;
@@ -982,7 +1055,7 @@ mod tests {
982
1055
] ;
983
1056
984
1057
for ( src, should_be) in inputs {
985
- let got = build_header_links ( src) ;
1058
+ let got = build_header_links ( src, None ) ;
986
1059
assert_eq ! ( got, should_be) ;
987
1060
}
988
1061
}
0 commit comments