language_win.cpp 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include "spellcheck/platform/win/language_win.h"
  8. #include "base/platform/win/base_windows_safe_library.h"
  9. #include <Windows.h>
  10. #include <ElsCore.h>
  11. #include <ElsSrvc.h> // ELS_GUID_LANGUAGE_DETECTION.
  12. namespace Platform::Language {
  13. namespace {
  14. HRESULT (__stdcall *MappingGetServices)(
  15. _In_opt_ PMAPPING_ENUM_OPTIONS pOptions,
  16. _Out_ PMAPPING_SERVICE_INFO *prgServices,
  17. _Out_ DWORD *pdwServicesCount
  18. );
  19. HRESULT (__stdcall *MappingFreeServices)(
  20. _In_ PMAPPING_SERVICE_INFO pServiceInfo
  21. );
  22. HRESULT (__stdcall *MappingRecognizeText)(
  23. _In_ PMAPPING_SERVICE_INFO pServiceInfo,
  24. _In_reads_(dwLength) LPCWSTR pszText,
  25. _In_ DWORD dwLength,
  26. _In_ DWORD dwIndex,
  27. _In_opt_ PMAPPING_OPTIONS pOptions,
  28. _Inout_ PMAPPING_PROPERTY_BAG pbag
  29. );
  30. HRESULT (__stdcall *MappingFreePropertyBag)(
  31. _In_ PMAPPING_PROPERTY_BAG pBag
  32. );
  33. [[nodiscard]] inline bool Supported() {
  34. static const auto Result = [] {
  35. #define LOAD_SYMBOL(lib, name) base::Platform::LoadMethod(lib, #name, name)
  36. const auto els = base::Platform::SafeLoadLibrary(L"elscore.dll");
  37. return LOAD_SYMBOL(els, MappingGetServices)
  38. && LOAD_SYMBOL(els, MappingRecognizeText)
  39. && LOAD_SYMBOL(els, MappingFreeServices)
  40. && LOAD_SYMBOL(els, MappingFreePropertyBag);
  41. #undef LOAD_SYMBOL
  42. }();
  43. return Result;
  44. }
  45. struct unique_services final {
  46. operator MAPPING_SERVICE_INFO *() {
  47. return services;
  48. }
  49. MAPPING_SERVICE_INFO **operator&() {
  50. return &services;
  51. }
  52. MAPPING_SERVICE_INFO *services = nullptr;
  53. unique_services() = default;
  54. unique_services(unique_services const &other) = delete;
  55. ~unique_services() {
  56. if (services) {
  57. MappingFreeServices(services);
  58. }
  59. }
  60. };
  61. struct unique_bag final : public MAPPING_PROPERTY_BAG {
  62. unique_bag() : MAPPING_PROPERTY_BAG{} {
  63. }
  64. unique_bag(unique_bag const &other) = delete;
  65. ~unique_bag() {
  66. MappingFreePropertyBag(this);
  67. }
  68. };
  69. inline void MappingRecognizeTextFromService(
  70. REFGUID service,
  71. LPCWSTR text,
  72. DWORD length,
  73. unique_bag &bag) {
  74. auto options = MAPPING_ENUM_OPTIONS{};
  75. options.Size = sizeof(options);
  76. options.pGuid = const_cast<GUID*>(&service);
  77. auto dwServicesCount = DWORD(0);
  78. auto services = unique_services();
  79. const auto hr = MappingGetServices(&options, &services, &dwServicesCount);
  80. if (FAILED(hr)) {
  81. return;
  82. }
  83. bag.Size = sizeof(bag);
  84. MappingRecognizeText(services, text, length, 0, nullptr, &bag);
  85. }
  86. } // namespace
  87. void RecognizeTextLanguages(
  88. LPCWSTR text,
  89. DWORD length,
  90. Fn<void(LPCWSTR, int)> &&callback) {
  91. if (!length) {
  92. return;
  93. }
  94. auto bag = unique_bag();
  95. MappingRecognizeTextFromService(
  96. ELS_GUID_LANGUAGE_DETECTION,
  97. text,
  98. length,
  99. bag);
  100. auto pos = reinterpret_cast<LPCWSTR>(bag.prgResultRanges[0].pData);
  101. for (; *pos;) {
  102. const auto len = wcslen(pos);
  103. if (len >= 2) {
  104. callback(pos, len);
  105. }
  106. pos += (len + 1);
  107. }
  108. }
  109. Id Recognize(QStringView text) {
  110. if (Supported()) {
  111. auto locales = std::vector<QLocale>();
  112. RecognizeTextLanguages(
  113. (LPCWSTR)text.utf16(),
  114. DWORD(text.size()),
  115. [&](LPCWSTR r, int length) {
  116. // Cut complex result, e.g. "sr-Cyrl".
  117. locales.emplace_back(QString::fromWCharArray(r, 2));
  118. });
  119. return { locales[0].language() };
  120. }
  121. return {};
  122. }
  123. } // namespace Platform::Language