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