| /* | |
| * Copyright 2025 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "include/core/SkString.h" | |
| #include "modules/skunicode/include/SkUnicode.h" | |
| #if defined(SK_UNICODE_BIDI_IMPLEMENTATION) | |
| #include "modules/skunicode/include/SkUnicode_bidi.h" | |
| #else | |
| #error "SkUnicode bidi component is required but missing" | |
| #endif | |
| #include <string> | |
| #include <vector> | |
| #include <emscripten.h> | |
| #include <emscripten/bind.h> | |
| #include "modules/canvaskit/WasmCommon.h" | |
| using namespace emscripten; | |
| using namespace skia_private; | |
| void JSArrayFromBidiRegions(JSArray& array, std::vector<SkUnicode::BidiRegion>& regions) { | |
| for (auto region : regions) { | |
| array.call<void>("push", region.start); | |
| array.call<void>("push", region.end); | |
| array.call<void>("push", (int32_t)region.level); | |
| } | |
| } | |
| class BidiPlaceholder { }; | |
| class CodeUnitsPlaceholder { }; | |
| static sk_sp<SkUnicode> getBidiUnicode() { | |
| static SkUnicode* unicode = SkUnicodes::Bidi::Make().release(); | |
| return sk_ref_sp(unicode); | |
| } | |
| EMSCRIPTEN_BINDINGS(Bidi) { | |
| class_<BidiPlaceholder>("Bidi") | |
| .class_function("_getBidiRegions", | |
| optional_override([](JSString jtext, int dir) -> JSArray { | |
| std::u16string textStorage = jtext.as<std::u16string>(); | |
| const char16_t* text = textStorage.data(); | |
| size_t textCount = textStorage.size(); | |
| JSArray result = emscripten::val::array(); | |
| std::vector<SkUnicode::BidiRegion> regions; | |
| SkBidiIterator::Direction direction = | |
| dir == 1 ? SkBidiIterator::Direction::kLTR | |
| : SkBidiIterator::Direction::kRTL; | |
| getBidiUnicode()->forEachBidiRegion((const uint16_t*)text, textCount, direction, | |
| [&](uint16_t start, uint16_t end, SkBidiIterator::Level level) { | |
| regions.emplace_back(start, end, level); | |
| }); | |
| JSArrayFromBidiRegions(result, regions); | |
| return result; | |
| }), | |
| allow_raw_pointers()) | |
| .class_function("_reorderVisual", | |
| optional_override([](WASMPointerU8 runLevels, | |
| int levelsCount) -> JSArray { | |
| // Convert WASMPointerU8 to std::vector<SkUnicode::BidiLevel> | |
| SkUnicode::BidiLevel* data = reinterpret_cast<SkUnicode::BidiLevel*>(runLevels); | |
| // The resulting vector | |
| std::vector<int32_t> logicalFromVisual; | |
| logicalFromVisual.resize(levelsCount); | |
| getBidiUnicode()->reorderVisual(data, levelsCount, logicalFromVisual.data()); | |
| // Convert std::vector<int32_t> to JSArray | |
| JSArray result = emscripten::val::array(); | |
| for (auto logical : logicalFromVisual) { | |
| result.call<void>("push", logical); | |
| } | |
| return result; | |
| }), | |
| allow_raw_pointers()); | |
| class_<CodeUnitsPlaceholder>("CodeUnits") | |
| .class_function("_compute", | |
| optional_override([](JSString jtext) -> JSArray { | |
| std::u16string textStorage = jtext.as<std::u16string>(); | |
| char16_t * text = textStorage.data(); | |
| size_t textCount = textStorage.size(); | |
| skia_private::TArray<SkUnicode::CodeUnitFlags, true> flags; | |
| flags.resize(textCount); | |
| JSArray result = emscripten::val::array(); | |
| if (!getBidiUnicode()->computeCodeUnitFlags( | |
| text, textCount, /*replaceTabs=*/false, &flags)) { | |
| return result; | |
| } | |
| for (auto flag : flags) { | |
| result.call<void>("push", (uint16_t)flag); | |
| } | |
| return result; | |
| }), | |
| allow_raw_pointers()); | |
| } |