@@ -1699,8 +1699,8 @@ pub fn escape(output: &mut dyn Write, buffer: &str) -> fmt::Result {
16991699/// * U+0027 APOSTROPHE ' is rendered as '
17001700/// * Alphanumeric and a range of non-URL safe characters.
17011701///
1702- /// The inclusion of characters like "%" in those which are not escaped is
1703- /// explained somewhat here :
1702+ /// Note that we leave "%" alone if it is followed by two hexdigits.
1703+ /// See :
17041704///
17051705/// <https://github.com/github/cmark-gfm/blob/c32ef78bae851cb83b7ad52d0fbff880acdcd44a/src/houdini_href_e.c#L7-L31>
17061706///
@@ -1721,7 +1721,7 @@ pub fn escape(output: &mut dyn Write, buffer: &str) -> fmt::Result {
17211721/// or `https` are permitted.
17221722pub fn escape_href ( output : & mut dyn Write , buffer : & str , relaxed_ipv6 : bool ) -> fmt:: Result {
17231723 const HREF_SAFE : [ bool ; 256 ] = character_set ! (
1724- b"-_.+!*(),% #@?=;:/,+$~" ,
1724+ b"-_.+!*(),#@?=;:/,+$~" ,
17251725 b"abcdefghijklmnopqrstuvwxyz" ,
17261726 b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
17271727 ) ;
@@ -1761,6 +1761,16 @@ pub fn escape_href(output: &mut dyn Write, buffer: &str, relaxed_ipv6: bool) ->
17611761 b'\'' => {
17621762 output. write_str ( "'" ) ?;
17631763 }
1764+ b'%' => {
1765+ if bytes. get ( i + 1 ) . map_or ( false , |b| b. is_ascii_hexdigit ( ) )
1766+ && bytes. get ( i + 2 ) . map_or ( false , |b| b. is_ascii_hexdigit ( ) )
1767+ {
1768+ output. write_str ( & buffer[ i..=i + 2 ] ) ?;
1769+ i += 2 ;
1770+ } else {
1771+ output. write_str ( "%25" ) ?;
1772+ }
1773+ }
17641774 0 => {
17651775 // U+FFFD REPLACEMENT CHARACTER
17661776 output. write_str ( "%EF%BF%BD" ) ?;
0 commit comments