@@ -52,67 +52,105 @@ internal class CssPropertyNormalizer {
5252 /// <param name="str">the property</param>
5353 /// <returns>the normalized property</returns>
5454 public static String Normalize ( String str ) {
55- StringBuilder buffer = new StringBuilder ( ) ;
56- int segmentStart = 0 ;
57- for ( int i = 0 ; i < str . Length ; ++ i ) {
55+ StringBuilder sb = new StringBuilder ( ) ;
56+ bool isWhitespace = false ;
57+ int i = 0 ;
58+ while ( i < str . Length ) {
5859 if ( str [ i ] == '\\ ' ) {
60+ sb . Append ( str [ i ] ) ;
5961 ++ i ;
60- }
61- else {
62- if ( str [ i ] == '\' ' || str [ i ] == '"' ) {
63- AppendAndFormatSegment ( buffer , str , segmentStart , i + 1 ) ;
64- segmentStart = i = AppendQuoteContent ( buffer , str , i + 1 , str [ i ] ) ;
62+ if ( i < str . Length ) {
63+ sb . Append ( str [ i ] ) ;
64+ ++ i ;
6565 }
6666 }
67- }
68- if ( segmentStart < str . Length ) {
69- AppendAndFormatSegment ( buffer , str , segmentStart , str . Length ) ;
70- }
71- return buffer . ToString ( ) ;
72- }
73-
74- /// <summary>Appends and formats a segment.</summary>
75- /// <param name="buffer">the current buffer</param>
76- /// <param name="source">a source</param>
77- /// <param name="start">where to start in the source</param>
78- /// <param name="end">where to end in the source</param>
79- private static void AppendAndFormatSegment ( StringBuilder buffer , String source , int start , int end ) {
80- String [ ] parts = iText . IO . Util . StringUtil . Split ( source . JSubstring ( start , end ) , "\\ s" ) ;
81- StringBuilder sb = new StringBuilder ( ) ;
82- foreach ( String part in parts ) {
83- if ( part . Length > 0 ) {
84- if ( sb . Length > 0 && ! TrimSpaceAfter ( sb [ sb . Length - 1 ] ) && ! TrimSpaceBefore ( part [ 0 ] ) ) {
85- sb . Append ( " " ) ;
86- }
87- // Do not make base64 data lowercase, function name only
88- if ( part . Matches ( "^[uU][rR][lL]\\ (.+\\ )" ) && CssUtils . IsBase64Data ( part . JSubstring ( 4 , part . Length - 1 ) ) ) {
89- sb . Append ( part . JSubstring ( 0 , 3 ) . ToLowerInvariant ( ) ) . Append ( part . Substring ( 3 ) ) ;
67+ else {
68+ if ( iText . IO . Util . TextUtil . IsWhiteSpace ( str [ i ] ) ) {
69+ isWhitespace = true ;
70+ ++ i ;
9071 }
9172 else {
92- sb . Append ( part . ToLowerInvariant ( ) ) ;
73+ if ( isWhitespace ) {
74+ if ( sb . Length > 0 && ! TrimSpaceAfter ( sb [ sb . Length - 1 ] ) && ! TrimSpaceBefore ( str [ i ] ) ) {
75+ sb . Append ( " " ) ;
76+ }
77+ isWhitespace = false ;
78+ }
79+ if ( str [ i ] == '\' ' || str [ i ] == '"' ) {
80+ i = AppendQuotedString ( sb , str , i ) ;
81+ }
82+ else {
83+ if ( ( str [ i ] == 'u' || str [ i ] == 'U' ) && str . Substring ( i ) . Matches ( "^[uU][rR][lL]\\ (.*?" ) ) {
84+ sb . Append ( str . JSubstring ( i , i + 4 ) . ToLowerInvariant ( ) ) ;
85+ i = AppendUrlContent ( sb , str , i + 4 ) ;
86+ }
87+ else {
88+ sb . Append ( char . ToLower ( str [ i ] ) ) ;
89+ ++ i ;
90+ }
91+ }
9392 }
9493 }
9594 }
96- buffer . Append ( sb ) ;
95+ return sb . ToString ( ) ;
9796 }
9897
99- /// <summary>Appends quoted content .</summary>
98+ /// <summary>Appends quoted string .</summary>
10099 /// <param name="buffer">the current buffer</param>
101100 /// <param name="source">a source</param>
102- /// <param name="start">where to start in the source</param>
103- /// <param name="endQuoteSymbol">the end quote symbol</param>
101+ /// <param name="start">where to start in the source. Should point at quote symbol.</param>
104102 /// <returns>the new position in the source</returns>
105- private static int AppendQuoteContent ( StringBuilder buffer , String source , int start , char endQuoteSymbol ) {
106- int end = CssUtils . FindNextUnescapedChar ( source , endQuoteSymbol , start ) ;
103+ private static int AppendQuotedString ( StringBuilder buffer , String source , int start ) {
104+ char endQuoteSymbol = source [ start ] ;
105+ int end = CssUtils . FindNextUnescapedChar ( source , endQuoteSymbol , start + 1 ) ;
107106 if ( end == - 1 ) {
108107 end = source . Length ;
109108 LoggerFactory . GetLogger ( typeof ( CssPropertyNormalizer ) ) . Warn ( MessageFormatUtil . Format ( iText . Html2pdf . LogMessageConstant
110109 . QUOTE_IS_NOT_CLOSED_IN_CSS_EXPRESSION , source ) ) ;
111110 }
111+ else {
112+ ++ end ;
113+ }
112114 buffer . JAppend ( source , start , end ) ;
113115 return end ;
114116 }
115117
118+ /// <summary>Appends url content and end parenthesis if url is correct.</summary>
119+ /// <param name="buffer">the current buffer</param>
120+ /// <param name="source">a source</param>
121+ /// <param name="start">where to start in the source. Should point at first symbol after "url(".</param>
122+ /// <returns>the new position in the source</returns>
123+ private static int AppendUrlContent ( StringBuilder buffer , String source , int start ) {
124+ while ( iText . IO . Util . TextUtil . IsWhiteSpace ( source [ start ] ) && start < source . Length ) {
125+ ++ start ;
126+ }
127+ if ( start < source . Length ) {
128+ int curr = start ;
129+ if ( source [ curr ] == '"' || source [ curr ] == '\' ' ) {
130+ curr = AppendQuotedString ( buffer , source , curr ) ;
131+ return curr ;
132+ }
133+ else {
134+ curr = CssUtils . FindNextUnescapedChar ( source , ')' , curr ) ;
135+ if ( curr == - 1 ) {
136+ LoggerFactory . GetLogger ( typeof ( CssPropertyNormalizer ) ) . Warn ( MessageFormatUtil . Format ( iText . Html2pdf . LogMessageConstant
137+ . URL_IS_NOT_CLOSED_IN_CSS_EXPRESSION , source ) ) ;
138+ return source . Length ;
139+ }
140+ else {
141+ buffer . Append ( source . JSubstring ( start , curr ) . Trim ( ) ) ;
142+ buffer . Append ( ')' ) ;
143+ return curr + 1 ;
144+ }
145+ }
146+ }
147+ else {
148+ LoggerFactory . GetLogger ( typeof ( CssPropertyNormalizer ) ) . Warn ( MessageFormatUtil . Format ( iText . Html2pdf . LogMessageConstant
149+ . URL_IS_EMPTY_IN_CSS_EXPRESSION , source ) ) ;
150+ return source . Length ;
151+ }
152+ }
153+
116154 /// <summary>Checks if spaces can be trimmed after a specific character.</summary>
117155 /// <param name="ch">the character</param>
118156 /// <returns>true, if spaces can be trimmed after the character</returns>
0 commit comments