@@ -56,16 +56,38 @@ impl HtmlHandlebars {
56
56
57
57
let content = utils:: render_markdown ( & ch. content , ctx. html_config . curly_quotes ) ;
58
58
59
- let fixed_content =
60
- utils:: render_markdown_with_path ( & ch. content , ctx. html_config . curly_quotes , Some ( path) ) ;
59
+ let printed_item = utils:: render_markdown_with_path_and_redirects (
60
+ & ch. content ,
61
+ ctx. html_config . curly_quotes ,
62
+ Some ( path) ,
63
+ & ctx. html_config . redirect ,
64
+ ) ;
61
65
if !ctx. is_index && ctx. html_config . print . page_break {
62
66
// Add page break between chapters
63
67
// See https://developer.mozilla.org/en-US/docs/Web/CSS/break-before and https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-before
64
68
// Add both two CSS properties because of the compatibility issue
65
69
print_content
66
70
. push_str ( r#"<div style="break-before: page; page-break-before: always;"></div>"# ) ;
67
71
}
68
- 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
+ ) ) ;
69
91
70
92
// Update the context with data for this file
71
93
let ctx_path = path
@@ -210,7 +232,23 @@ impl HtmlHandlebars {
210
232
code_config : & Code ,
211
233
edition : Option < RustEdition > ,
212
234
) -> String {
213
- let rendered = build_header_links ( & rendered) ;
235
+ let rendered = build_header_links ( & rendered, None ) ;
236
+ let rendered = self . post_process_common ( rendered, & playground_config, code_config, edition) ;
237
+
238
+ rendered
239
+ }
240
+
241
+ /// Applies some post-processing to the HTML to apply some adjustments.
242
+ ///
243
+ /// This common function is used for both normal chapters (via
244
+ /// `post_process`) and the combined print page.
245
+ fn post_process_common (
246
+ & self ,
247
+ rendered : String ,
248
+ playground_config : & Playground ,
249
+ code_config : & Code ,
250
+ edition : Option < RustEdition > ,
251
+ ) -> String {
214
252
let rendered = fix_code_blocks ( & rendered) ;
215
253
let rendered = add_playground_pre ( & rendered, playground_config, edition) ;
216
254
let rendered = hide_lines ( & rendered, code_config) ;
@@ -568,7 +606,7 @@ impl Renderer for HtmlHandlebars {
568
606
debug ! ( "Render template" ) ;
569
607
let rendered = handlebars. render ( "index" , & data) ?;
570
608
571
- let rendered = self . post_process (
609
+ let rendered = self . post_process_common (
572
610
rendered,
573
611
& html_config. playground ,
574
612
& html_config. code ,
@@ -779,9 +817,34 @@ fn make_data(
779
817
Ok ( data)
780
818
}
781
819
820
+ /// Go through the rendered print page HTML,
821
+ /// add path id prefix to all the elements id as well as footnote links.
822
+ fn build_print_element_id ( html : & str , print_page_id : & str ) -> String {
823
+ static ALL_ID : Lazy < Regex > = Lazy :: new ( || Regex :: new ( r#"(<[^>]*?id=")([^"]+?)""# ) . unwrap ( ) ) ;
824
+ static FOOTNOTE_ID : Lazy < Regex > = Lazy :: new ( || {
825
+ Regex :: new (
826
+ r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""## ,
827
+ )
828
+ . unwrap ( )
829
+ } ) ;
830
+
831
+ let temp_html = ALL_ID . replace_all ( html, |caps : & Captures < ' _ > | {
832
+ format ! ( "{}{}-{}\" " , & caps[ 1 ] , print_page_id, & caps[ 2 ] )
833
+ } ) ;
834
+
835
+ FOOTNOTE_ID
836
+ . replace_all ( & temp_html, |caps : & Captures < ' _ > | {
837
+ format ! ( "{}{}-{}\" " , & caps[ 1 ] , print_page_id, & caps[ 2 ] )
838
+ } )
839
+ . into_owned ( )
840
+ }
841
+
782
842
/// Goes through the rendered HTML, making sure all header tags have
783
843
/// an anchor respectively so people can link to sections directly.
784
- fn build_header_links ( html : & str ) -> String {
844
+ ///
845
+ /// `print_page_id` should be set to the print page ID prefix when adjusting the
846
+ /// print page.
847
+ fn build_header_links ( html : & str , print_page_id : Option < & str > ) -> String {
785
848
static BUILD_HEADER_LINKS : Lazy < Regex > = Lazy :: new ( || {
786
849
Regex :: new ( r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"# ) . unwrap ( )
787
850
} ) ;
@@ -810,21 +873,34 @@ fn build_header_links(html: &str) -> String {
810
873
caps. get ( 2 ) . map ( |x| x. as_str ( ) . to_string ( ) ) ,
811
874
caps. get ( 3 ) . map ( |x| x. as_str ( ) . to_string ( ) ) ,
812
875
& mut id_counter,
876
+ print_page_id,
813
877
)
814
878
} )
815
879
. into_owned ( )
816
880
}
817
881
818
882
/// Insert a sinle link into a header, making sure each link gets its own
819
883
/// unique ID by appending an auto-incremented number (if necessary).
884
+ ///
885
+ /// For `print.html`, we will add a path id prefix.
820
886
fn insert_link_into_header (
821
887
level : usize ,
822
888
content : & str ,
823
889
id : Option < String > ,
824
890
classes : Option < String > ,
825
891
id_counter : & mut HashMap < String , usize > ,
892
+ print_page_id : Option < & str > ,
826
893
) -> String {
827
- let id = id. unwrap_or_else ( || utils:: unique_id_from_content ( content, id_counter) ) ;
894
+ let id = if let Some ( print_page_id) = print_page_id {
895
+ let content_id = {
896
+ #[ allow( deprecated) ]
897
+ utils:: id_from_content ( content)
898
+ } ;
899
+ let with_prefix = format ! ( "{} {}" , print_page_id, content_id) ;
900
+ id. unwrap_or_else ( || utils:: unique_id_from_content ( & with_prefix, id_counter) )
901
+ } else {
902
+ id. unwrap_or_else ( || utils:: unique_id_from_content ( content, id_counter) )
903
+ } ;
828
904
let classes = classes
829
905
. map ( |s| format ! ( " class=\" {s}\" " ) )
830
906
. unwrap_or_default ( ) ;
@@ -1113,7 +1189,7 @@ mod tests {
1113
1189
] ;
1114
1190
1115
1191
for ( src, should_be) in inputs {
1116
- let got = build_header_links ( src) ;
1192
+ let got = build_header_links ( src, None ) ;
1117
1193
assert_eq ! ( got, should_be) ;
1118
1194
}
1119
1195
}
0 commit comments