// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "encodings/compact_lang_det/win/normalizedunicodetext.h" #include #include #include #include "encodings/compact_lang_det/win/cld_scopedptr.h" namespace { // Function prototypes copied from MSDN. typedef BOOL (WINAPI *IsNormalizedStringFunction)(NORM_FORM NormForm, LPCWSTR lpSrcString, int cwSrcLength); typedef int (WINAPI *NormalizeStringFunction)(NORM_FORM NormForm, LPCWSTR lpSrcString, int cwSrcLength, LPWSTR lpDstString, int cwDstLength); // A class to provide an access to Normaliz.dll functions. // New normalization API implemented in Normaliz.dll is available starting // from Windows XP SP2, that's why we have to bind to it dynamically. class NormalizationAPI { public: // Creates fully initialized NormalizationAPI object. // Loads DLL and binds all referenced functions. NormalizationAPI() : library_(_T("Normaliz.dll")) { if (library_.IsValid()) { is_normalized_string_.Bind(library_.handle(), "IsNormalizedString"); normalize_string_.Bind(library_.handle(), "NormalizeString"); } } // Proxy functions for the ones loaded from DLL. BOOL IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpSrcString, int cwSrcLength) { if (!is_normalized_string_.IsValid()) return FALSE; return is_normalized_string_.function()(NormForm, lpSrcString, cwSrcLength); } int NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, int cwSrcLength, LPWSTR lpDstString, int cwDstLength) { if (!normalize_string_.IsValid()) { ::SetLastError(ERROR_INVALID_FUNCTION); return 0; } return normalize_string_.function()(NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength); } // Returns true if all functions were bound successfully. // This implies that library_ itself was loaded successfully. bool IsValid() const { return is_normalized_string_.IsValid() && normalize_string_.IsValid(); } private: // Holds a handle to loaded Normaliz.dll. ScopedLibrary library_; // Pointers to the functions loaded from Normaliz.dll. FunctionFromDll is_normalized_string_; FunctionFromDll normalize_string_; DISALLOW_COPY_AND_ASSIGN(NormalizationAPI); }; static NormalizationAPI normalization_api; } // namespace // NormalizedUnicodeText NormalizedUnicodeText::NormalizedUnicodeText() : normalized_text_(NULL) { } DWORD NormalizedUnicodeText::Normalize(NORM_FORM normalization_form, const WCHAR* text) { DWORD result = 0; normalized_text_ = TryToNormalizeText(normalization_form, text, &result); return result; } const WCHAR* NormalizedUnicodeText::TryToNormalizeText( NORM_FORM normalization_form, const WCHAR* text, DWORD *error_code) { if (!text) { text_.reset(); return text; } _ASSERT(NULL != error_code); if (!error_code) return text; if (!normalization_api.IsValid()) { // Fall back to the previous version of normalization API. int folded_text_size = ::FoldStringW(MAP_PRECOMPOSED, text, -1, NULL, 0); if (!folded_text_size) { *error_code = ::GetLastError(); return text; } text_.reset(new WCHAR[folded_text_size]); if (!text_.get()) { *error_code = ERROR_OUTOFMEMORY; return text; } int folding_result = ::FoldStringW(MAP_PRECOMPOSED, text, -1, text_.get(), folded_text_size); if (!folding_result) { *error_code = ::GetLastError(); text_.reset(); return text; } return text_.get(); } // No need to allocate anything when text is already normalized. if (normalization_api.IsNormalizedString(normalization_form, text, -1)) return text; // Get the first approximation for the buffer size required to store // normalized text. int normalized_text_size_guess = normalization_api.NormalizeString(normalization_form, text, -1, NULL, 0); while (normalized_text_size_guess > 0) { text_.reset(new WCHAR[normalized_text_size_guess]); if (!text_.get()) { *error_code = ERROR_OUTOFMEMORY; break; } int normalized_text_size = normalization_api.NormalizeString(normalization_form, text, -1, text_.get(), normalized_text_size_guess); if (normalized_text_size > 0) { // Text was successfully converted. return text_.get(); } if (ERROR_INSUFFICIENT_BUFFER != ::GetLastError()) { *error_code = ::GetLastError(); // Text cannot be normalized, use the original. // By the way, ERROR_SUCCESS is a puzzling case. // MSDN says 'The action completed successfully but yielded no results'. // Does this mean that output buffer was not changed? // Anyway, just in case, also return the original text. break; } // Try again with the corrected buffer size. normalized_text_size_guess = -normalized_text_size; } // Use the original text in case of any problem with normalization. text_.reset(); return text; }