/* Entry points for johnmcm.com's WordPerfect viewer: libwpd and librevenge * compiled to WebAssembly. The same calls, and the same answers, as the * Works Viewer's module (wasm-src/wps/src/binding.cpp), so the page reads * both with one engine. * * SPDX-License-Identifier: MPL-2.0 * * Every entry point returns a plain object instead of throwing, so the page * never handles a WebAssembly.Exception. * * view = inputBuffer(n) Uint8Array over n bytes of wasm memory; fill it at once * d = detect() { ok, kind, creator, encrypted, needEncoding } * kind is always 0 (text); creator 100 (WordPerfect) * r = parse(password, enc) { ok, result, kind, json } * result in the Works module's numbering: 0 OK, * 1 password missing or wrong, 2 file access, * 3 parse, 4 OLE, 5 unknown, 6 unsupported encryption * release() frees the input */ #include #include #include #include #include #include #include #include #include #include "recorder.h" using emscripten::val; namespace { unsigned char *g_input = nullptr; size_t g_size = 0; void release() { std::free(g_input); g_input = nullptr; g_size = 0; } val fail(const std::string &msg) { val r = val::object(); r.set("ok", false); r.set("error", msg); return r; } val inputBuffer(unsigned size) { release(); if (!size) return val::null(); g_input = static_cast(std::malloc(size)); if (!g_input) return val::null(); g_size = size; return val(emscripten::typed_memory_view(size, g_input)); } val detect() { if (!g_input) return fail("no input"); try { librevenge::RVNGStringStream input(g_input, static_cast(g_size)); libwpd::WPDConfidence confidence = libwpd::WPDocument::isFileFormatSupported(&input); val r = val::object(); r.set("ok", confidence != libwpd::WPD_CONFIDENCE_NONE); r.set("kind", 0); r.set("creator", 100); r.set("encrypted", confidence == libwpd::WPD_CONFIDENCE_SUPPORTED_ENCRYPTION || confidence == libwpd::WPD_CONFIDENCE_UNSUPPORTED_ENCRYPTION); r.set("unsupportedEncryption", confidence == libwpd::WPD_CONFIDENCE_UNSUPPORTED_ENCRYPTION); r.set("needEncoding", false); return r; } catch (const std::exception &e) { return fail(std::string("C++ exception: ") + e.what()); } catch (...) { return fail("unknown C++ exception"); } } int resultCode(libwpd::WPDResult result) { switch (result) { case libwpd::WPD_OK: return 0; case libwpd::WPD_PASSWORD_MISSMATCH_ERROR: return 1; case libwpd::WPD_FILE_ACCESS_ERROR: return 2; case libwpd::WPD_PARSE_ERROR: return 3; case libwpd::WPD_OLE_ERROR: return 4; case libwpd::WPD_UNSUPPORTED_ENCRYPTION_ERROR: return 6; default: return 5; } } val parse(std::string password, std::string /* encoding: WordPerfect files say their own */) { if (!g_input) return fail("no input"); try { librevenge::RVNGStringStream input(g_input, static_cast(g_size)); if (libwpd::WPDocument::isFileFormatSupported(&input) == libwpd::WPD_CONFIDENCE_NONE) return fail("unsupported"); input.seek(0, librevenge::RVNG_SEEK_SET); jm::TextRecorder rec; libwpd::WPDResult result = libwpd::WPDocument::parse(&input, &rec, password.empty() ? nullptr : password.c_str()); val r = val::object(); r.set("ok", result == libwpd::WPD_OK); r.set("result", resultCode(result)); r.set("kind", 0); r.set("json", rec.result()); return r; } catch (const std::exception &e) { return fail(std::string("C++ exception: ") + e.what()); } catch (...) { return fail("unknown C++ exception"); } } } // anonymous namespace EMSCRIPTEN_BINDINGS(wpd) { emscripten::function("inputBuffer", &inputBuffer); emscripten::function("detect", &detect); emscripten::function("parse", &parse); emscripten::function("release", &release); }