/* * 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 "include/private/base/SkOnce.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 #include #include #include #include "modules/canvaskit/WasmCommon.h" using namespace emscripten; using namespace skia_private; void JSArrayFromBidiRegions(JSArray& array, std::vector& regions) { for (auto region : regions) { array.call("push", region.start); array.call("push", region.end); array.call("push", (int32_t)region.level); } } class BidiPlaceholder { }; class CodeUnitsPlaceholder { }; static sk_sp getClientUnicode() { static sk_sp unicode; static SkOnce once; once([] { unicode = SkUnicodes::Bidi::Make(); }); return unicode; } EMSCRIPTEN_BINDINGS(Bidi) { class_("Bidi") .class_function("_getBidiRegions", optional_override([](JSString jtext, int dir) -> JSArray { std::u16string textStorage = jtext.as(); const char16_t* text = textStorage.data(); size_t textCount = textStorage.size(); JSArray result = emscripten::val::array(); std::vector regions; SkBidiIterator::Direction direction = dir == 1 ? SkBidiIterator::Direction::kLTR : SkBidiIterator::Direction::kRTL; getClientUnicode()->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* data = reinterpret_cast(runLevels); // The resulting vector std::vector logicalFromVisual; logicalFromVisual.resize(levelsCount); getClientUnicode()->reorderVisual(data, levelsCount, logicalFromVisual.data()); // Convert std::vector to JSArray JSArray result = emscripten::val::array(); for (auto logical : logicalFromVisual) { result.call("push", logical); } return result; }), allow_raw_pointers()); class_("CodeUnits") .class_function("_compute", optional_override([](JSString jtext) -> JSArray { std::u16string textStorage = jtext.as(); char16_t * text = textStorage.data(); size_t textCount = textStorage.size(); skia_private::TArray flags; flags.resize(textCount); JSArray result = emscripten::val::array(); if (!getClientUnicode()->computeCodeUnitFlags( text, textCount, /*replaceTabs=*/false, &flags)) { return result; } for (auto flag : flags) { result.call("push", (uint16_t)flag); } return result; }), allow_raw_pointers()); }