• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2025 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 #include "include/core/SkString.h"
8 #include "include/private/base/SkOnce.h"
9 #include "modules/skunicode/include/SkUnicode.h"
10 
11 #if defined(SK_UNICODE_BIDI_IMPLEMENTATION)
12 #include "modules/skunicode/include/SkUnicode_bidi.h"
13 #else
14 #error "SkUnicode bidi component is required but missing"
15 #endif
16 
17 #include <string>
18 #include <vector>
19 
20 #include <emscripten.h>
21 #include <emscripten/bind.h>
22 #include "modules/canvaskit/WasmCommon.h"
23 
24 using namespace emscripten;
25 using namespace skia_private;
26 
JSArrayFromBidiRegions(JSArray & array,std::vector<SkUnicode::BidiRegion> & regions)27 void JSArrayFromBidiRegions(JSArray& array, std::vector<SkUnicode::BidiRegion>& regions) {
28     for (auto region : regions) {
29         array.call<void>("push", region.start);
30         array.call<void>("push", region.end);
31         array.call<void>("push", (int32_t)region.level);
32     }
33 }
34 
35 class BidiPlaceholder { };
36 
37 class CodeUnitsPlaceholder { };
38 
getClientUnicode()39 static sk_sp<SkUnicode> getClientUnicode() {
40     static sk_sp<SkUnicode> unicode;
41     static SkOnce once;
42     once([] { unicode = SkUnicodes::Bidi::Make(); });
43     return unicode;
44 }
45 
EMSCRIPTEN_BINDINGS(Bidi)46 EMSCRIPTEN_BINDINGS(Bidi) {
47     class_<BidiPlaceholder>("Bidi")
48         .class_function("_getBidiRegions",
49                   optional_override([](JSString jtext, int dir) -> JSArray {
50                       std::u16string textStorage = jtext.as<std::u16string>();
51                       const char16_t* text = textStorage.data();
52                       size_t textCount = textStorage.size();
53                       JSArray result = emscripten::val::array();
54                       std::vector<SkUnicode::BidiRegion> regions;
55                       SkBidiIterator::Direction direction =
56                               dir == 1 ? SkBidiIterator::Direction::kLTR
57                                        : SkBidiIterator::Direction::kRTL;
58                       getClientUnicode()->forEachBidiRegion((const uint16_t*)text, textCount, direction,
59                                                             [&](uint16_t start, uint16_t end, SkBidiIterator::Level level) {
60                                                                 regions.emplace_back(start, end, level);
61                                                             });
62                       JSArrayFromBidiRegions(result, regions);
63                       return result;
64                   }),
65                   allow_raw_pointers())
66 
67             .class_function("_reorderVisual",
68                 optional_override([](WASMPointerU8 runLevels,
69                                    int levelsCount) -> JSArray {
70                     // Convert WASMPointerU8 to std::vector<SkUnicode::BidiLevel>
71                     SkUnicode::BidiLevel* data = reinterpret_cast<SkUnicode::BidiLevel*>(runLevels);
72 
73                     // The resulting vector
74                     std::vector<int32_t> logicalFromVisual;
75                     logicalFromVisual.resize(levelsCount);
76                     getClientUnicode()->reorderVisual(data, levelsCount, logicalFromVisual.data());
77 
78                     // Convert std::vector<int32_t> to JSArray
79                     JSArray result = emscripten::val::array();
80                     for (auto logical : logicalFromVisual) {
81                         result.call<void>("push", logical);
82                     }
83                     return result;
84                 }),
85                 allow_raw_pointers());
86 
87     class_<CodeUnitsPlaceholder>("CodeUnits")
88         .class_function("_compute",
89             optional_override([](JSString jtext) -> JSArray {
90               std::u16string textStorage = jtext.as<std::u16string>();
91               char16_t * text = textStorage.data();
92               size_t textCount = textStorage.size();
93               skia_private::TArray<SkUnicode::CodeUnitFlags, true> flags;
94               flags.resize(textCount);
95               JSArray result = emscripten::val::array();
96               if (!getClientUnicode()->computeCodeUnitFlags(
97                           text, textCount, /*replaceTabs=*/false, &flags)) {
98                   return result;
99               }
100               for (auto flag : flags) {
101                   result.call<void>("push", (uint16_t)flag);
102               }
103               return result;
104             }),
105             allow_raw_pointers());
106 }
107