Skip to content

Commit

Permalink
Expose convertCharset convenience function to controller
Browse files Browse the repository at this point in the history
Allow to convert from UTF-8 to whatever encoding the device supports
  • Loading branch information
christophehenry committed Oct 27, 2024
1 parent 75a44d6 commit fb9ca37
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 2 deletions.
30 changes: 30 additions & 0 deletions res/controllers/engine-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,4 +301,34 @@ declare namespace engine {
* SoftStart with low factors would take a while until sound is audible. [default = 1.0]
*/
function softStart(deck: number, activate: boolean, factor?: number): void;

enum WellKnownCharsets {
Latin1,
ISO_8859_1,
Latin9,
ISO_8859_15,
UCS2,
ISO_10646_UCS_2
}

/**
* Converts a string into another charset.
*
* This function is useful to display text on a device that does not make use of UTF-8.
* Available charset names are listed here: http://www.iana.org/assignments/character-sets/character-sets.xhtml.
* Characters that are unsupported by target charset will be transformed to null character (0x00).
*
* @param targetCharset The charset to encode the string into.
* @param value The string to encode
* @returns The converted String as an array of bytes. Will return an empty buffer on conversion error.
*/
function convertCharset(targetCharset: string, value: string): ArrayBuffer

/**
* Version of {@link engine.convertCharset} to use with {@link engine.WellKnownCharsets}.
* @param targetCharset The charset to encode the string into.
* @param value The string to encode
* @returns The converted String as an array of bytes. Will return an empty buffer on conversion error.
*/
function convertCharset(targetCharset: WellKnownCharsets, value: string): ArrayBuffer
}
Original file line number Diff line number Diff line change
Expand Up @@ -415,8 +415,10 @@ bool ControllerScriptEngineLegacy::initialize() {
ControllerScriptInterfaceLegacy* legacyScriptInterface =
new ControllerScriptInterfaceLegacy(this, m_logger);

engineGlobalObject.setProperty(
"engine", m_pJSEngine->newQObject(legacyScriptInterface));
auto engine = m_pJSEngine->newQObject(legacyScriptInterface);
auto meta = m_pJSEngine->newQMetaObject(&ControllerScriptInterfaceLegacy::staticMetaObject);
engine.setProperty("WellKnownCharsets", meta);
engineGlobalObject.setProperty("engine", m_pJSEngine->newQObject(legacyScriptInterface));

#ifdef MIXXX_USE_QML
if (m_bQmlMode) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
#include "controllerscriptinterfacelegacy.h"

#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
#include <QTextCodec>
#else
#include <QStringEncoder>
#endif
#include <gsl/pointers>

#include "control/controlobject.h"
Expand Down Expand Up @@ -1047,3 +1052,47 @@ void ControllerScriptInterfaceLegacy::softStart(int deck, bool activate, double
// activate the ramping in scratchProcess()
m_ramp[deck] = true;
}

QByteArray ControllerScriptInterfaceLegacy::convertCharset(
const ControllerScriptInterfaceLegacy::WellKnownCharsets targetCharset,
const QString& value) {
switch (targetCharset) {
case WellKnownCharsets::Latin1:
case WellKnownCharsets::ISO_8859_1:
return convertCharset(QStringLiteral("ISO-8859-1"), value);
case WellKnownCharsets::Latin9:
case WellKnownCharsets::ISO_8859_15:
return convertCharset(QStringLiteral("ISO-8859-15"), value);
case WellKnownCharsets::UCS2:
case WellKnownCharsets::ISO_10646_UCS_2:
return convertCharset(QStringLiteral("ISO-10646-UCS-2"), value);
default:
m_pScriptEngineLegacy->logOrThrowError(QStringLiteral("Unknown charset specified"));
return QByteArray();
}
}

QByteArray ControllerScriptInterfaceLegacy::convertCharset(
const QString& targetCharset, const QString& value) {
#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
auto* pCodec = QTextCodec::codecForName(targetCharset.toUtf8());
if (!pCodec) {
m_pScriptEngineLegacy->logOrThrowError(QStringLiteral("Unable to open encoder"));
return QByteArray();
}
return pCodec->makeEncoder(QTextCodec::Flag::ConvertInvalidToNull)->fromUnicode(value);
#else
#if QT_VERSION > QT_VERSION_CHECK(6, 8, 0)
const auto* const encoderName = targetCharset.data();
#else
auto* const encoderName = targetCharset.toUtf8().data();
#endif
QStringEncoder fromUtf16 = QStringEncoder(
encoderName, QStringEncoder::Flag::ConvertInvalidToNull);
if (!fromUtf16.isValid()) {
m_pScriptEngineLegacy->logOrThrowError(QStringLiteral("Unable to open encoder"));
return QByteArray();
}
return fromUtf16(value);
#endif
}
17 changes: 17 additions & 0 deletions src/controllers/scripting/legacy/controllerscriptinterfacelegacy.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ class ConfigKey;
class ControllerScriptInterfaceLegacy : public QObject {
Q_OBJECT
public:
enum class WellKnownCharsets {
Latin1,
ISO_8859_1,
Latin9,
ISO_8859_15,
UCS2,
ISO_10646_UCS_2
};
Q_ENUM(WellKnownCharsets)

ControllerScriptInterfaceLegacy(ControllerScriptEngineLegacy* m_pEngine,
const RuntimeLoggingCategory& logger);

Expand Down Expand Up @@ -72,6 +82,13 @@ class ControllerScriptInterfaceLegacy : public QObject {
const double rate = -10.0);
Q_INVOKABLE void softStart(const int deck, bool activate, double factor = 1.0);

Q_INVOKABLE QByteArray convertCharset(
const ControllerScriptInterfaceLegacy::WellKnownCharsets
targetCharset,
const QString& value);

Q_INVOKABLE QByteArray convertCharset(const QString& targetCharset, const QString& value);

bool removeScriptConnection(const ScriptConnection& conn);
/// Execute a ScriptConnection's JS callback
void triggerScriptConnection(const ScriptConnection& conn);
Expand Down
37 changes: 37 additions & 0 deletions src/test/controllerscriptenginelegacy_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,43 @@ TEST_F(ControllerScriptEngineLegacyTest, connectionExecutesWithCorrectThisObject
EXPECT_DOUBLE_EQ(1.0, pass->get());
}

TEST_F(ControllerScriptEngineLegacyTest, convertCharsetUndefinedOnUnknownCharset) {
const auto result = evaluate("engine.convertCharset('NULL', 'Hello!')");

EXPECT_EQ(qjsvalue_cast<QByteArray>(result), QByteArrayLiteral(""));
}

template<int N>
QByteArray intByteArray(const char (&array)[N]) {
return QByteArray(array, N);
}

TEST_F(ControllerScriptEngineLegacyTest, convertCharsetCorrectValueWellKnown) {
const auto result = evaluate(
"engine.convertCharset(engine.WellKnownCharsets.Latin9, 'Hello!')");

// ISO-8859-15 ecoded 'Hello!'
EXPECT_EQ(qjsvalue_cast<QByteArray>(result),
intByteArray({0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21}));
}

TEST_F(ControllerScriptEngineLegacyTest, convertCharsetCorrectValueStringCharset) {
const auto result = evaluate("engine.convertCharset('ISO-8859-15', 'Hello!')");

// ISO-8859-15 ecoded 'Hello!'
EXPECT_EQ(qjsvalue_cast<QByteArray>(result),
intByteArray({0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21}));
}

TEST_F(ControllerScriptEngineLegacyTest, convertCharsetUnsupportedChars) {
auto result = qjsvalue_cast<QByteArray>(
evaluate("engine.convertCharset('ISO-8859-15', 'مايأ نامز')"));

EXPECT_EQ(result,
intByteArray(
{0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00}));
}

#ifdef MIXXX_USE_QML
class MockScreenRender : public ControllerRenderingEngine {
public:
Expand Down

0 comments on commit fb9ca37

Please sign in to comment.