1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "encodings/compact_lang_det/win/normalizedunicodetext.h" 6 7 #include <tchar.h> 8 #include <windows.h> 9 #include <winnls.h> 10 11 #include "encodings/compact_lang_det/win/cld_scopedptr.h" 12 13 14 namespace { 15 16 // Function prototypes copied from MSDN. 17 typedef BOOL (WINAPI *IsNormalizedStringFunction)(NORM_FORM NormForm, 18 LPCWSTR lpSrcString, 19 int cwSrcLength); 20 typedef int (WINAPI *NormalizeStringFunction)(NORM_FORM NormForm, 21 LPCWSTR lpSrcString, 22 int cwSrcLength, 23 LPWSTR lpDstString, 24 int cwDstLength); 25 26 // A class to provide an access to Normaliz.dll functions. 27 // New normalization API implemented in Normaliz.dll is available starting 28 // from Windows XP SP2, that's why we have to bind to it dynamically. 29 class NormalizationAPI { 30 public: 31 // Creates fully initialized NormalizationAPI object. 32 // Loads DLL and binds all referenced functions. 33 NormalizationAPI() 34 : library_(_T("Normaliz.dll")) { 35 if (library_.IsValid()) { 36 is_normalized_string_.Bind(library_.handle(), "IsNormalizedString"); 37 normalize_string_.Bind(library_.handle(), "NormalizeString"); 38 } 39 } 40 41 // Proxy functions for the ones loaded from DLL. 42 BOOL IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpSrcString, 43 int cwSrcLength) { 44 if (!is_normalized_string_.IsValid()) 45 return FALSE; 46 return is_normalized_string_.function()(NormForm, lpSrcString, cwSrcLength); 47 } 48 int NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, int cwSrcLength, 49 LPWSTR lpDstString, int cwDstLength) { 50 if (!normalize_string_.IsValid()) { 51 ::SetLastError(ERROR_INVALID_FUNCTION); 52 return 0; 53 } 54 return normalize_string_.function()(NormForm, lpSrcString, cwSrcLength, 55 lpDstString, cwDstLength); 56 } 57 58 // Returns true if all functions were bound successfully. 59 // This implies that library_ itself was loaded successfully. 60 bool IsValid() const { 61 return is_normalized_string_.IsValid() && normalize_string_.IsValid(); 62 } 63 64 private: 65 // Holds a handle to loaded Normaliz.dll. 66 ScopedLibrary library_; 67 // Pointers to the functions loaded from Normaliz.dll. 68 FunctionFromDll<IsNormalizedStringFunction> is_normalized_string_; 69 FunctionFromDll<NormalizeStringFunction> normalize_string_; 70 71 DISALLOW_COPY_AND_ASSIGN(NormalizationAPI); 72 }; 73 74 static NormalizationAPI normalization_api; 75 76 } // namespace 77 78 79 // NormalizedUnicodeText 80 81 NormalizedUnicodeText::NormalizedUnicodeText() 82 : normalized_text_(NULL) { 83 } 84 85 86 DWORD NormalizedUnicodeText::Normalize(NORM_FORM normalization_form, 87 const WCHAR* text) { 88 DWORD result = 0; 89 normalized_text_ = TryToNormalizeText(normalization_form, text, &result); 90 return result; 91 } 92 93 94 const WCHAR* NormalizedUnicodeText::TryToNormalizeText( 95 NORM_FORM normalization_form, const WCHAR* text, DWORD *error_code) { 96 if (!text) { 97 text_.reset(); 98 return text; 99 } 100 _ASSERT(NULL != error_code); 101 if (!error_code) 102 return text; 103 104 if (!normalization_api.IsValid()) { 105 // Fall back to the previous version of normalization API. 106 int folded_text_size = ::FoldStringW(MAP_PRECOMPOSED, text, -1, NULL, 0); 107 if (!folded_text_size) { 108 *error_code = ::GetLastError(); 109 return text; 110 } 111 112 text_.reset(new WCHAR[folded_text_size]); 113 if (!text_.get()) { 114 *error_code = ERROR_OUTOFMEMORY; 115 return text; 116 } 117 118 int folding_result = 119 ::FoldStringW(MAP_PRECOMPOSED, text, -1, text_.get(), folded_text_size); 120 if (!folding_result) { 121 *error_code = ::GetLastError(); 122 text_.reset(); 123 return text; 124 } 125 126 return text_.get(); 127 } 128 129 // No need to allocate anything when text is already normalized. 130 if (normalization_api.IsNormalizedString(normalization_form, text, -1)) 131 return text; 132 133 // Get the first approximation for the buffer size required to store 134 // normalized text. 135 int normalized_text_size_guess = 136 normalization_api.NormalizeString(normalization_form, text, -1, NULL, 0); 137 138 while (normalized_text_size_guess > 0) { 139 text_.reset(new WCHAR[normalized_text_size_guess]); 140 if (!text_.get()) { 141 *error_code = ERROR_OUTOFMEMORY; 142 break; 143 } 144 145 int normalized_text_size = 146 normalization_api.NormalizeString(normalization_form, text, -1, 147 text_.get(), 148 normalized_text_size_guess); 149 150 if (normalized_text_size > 0) { 151 // Text was successfully converted. 152 return text_.get(); 153 } 154 155 if (ERROR_INSUFFICIENT_BUFFER != ::GetLastError()) { 156 *error_code = ::GetLastError(); 157 // Text cannot be normalized, use the original. 158 // By the way, ERROR_SUCCESS is a puzzling case. 159 // MSDN says 'The action completed successfully but yielded no results'. 160 // Does this mean that output buffer was not changed? 161 // Anyway, just in case, also return the original text. 162 break; 163 } 164 165 // Try again with the corrected buffer size. 166 normalized_text_size_guess = -normalized_text_size; 167 } 168 169 // Use the original text in case of any problem with normalization. 170 text_.reset(); 171 return text; 172 } 173