Nnnn add extra functionality to string converter

adding three new converters to operate over strings:
- first one supports padding text with a set of characters
- second one removes trailing zeroes from a string
- third one trims spaces from text

several files are just the usual boilerplate code.
Added some comments in the code

Diffs=
2240d091f8 Nnnn add extra functionality to string converter (#8876)

Co-authored-by: hernan <hernan@rive.app>
diff --git a/.rive_head b/.rive_head
index b59df5e..93df727 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-5574d9e34ecfa4ba2790ff928e839fa854aa5b60
+2240d091f8d55ae4ef75409109684d9ca68b2450
diff --git a/dev/defs/data_bind/converters/data_converter_string_pad.json b/dev/defs/data_bind/converters/data_converter_string_pad.json
new file mode 100644
index 0000000..0b5f2b4
--- /dev/null
+++ b/dev/defs/data_bind/converters/data_converter_string_pad.json
@@ -0,0 +1,40 @@
+{
+    "name": "DataConverterStringPad",
+    "key": {
+      "int": 530,
+      "string": "dataconverterstringpad"
+    },
+    "extends": "data_bind/converters/data_converter.json",
+    "properties": {
+      "length": {
+        "type": "uint",
+        "initialValue": "1",
+        "key": {
+          "int": 743,
+          "string": "length"
+        },
+        "description": "Applies text pad depending on the padType until it reaches the given length",
+        "bindable": true
+      },
+      "text": {
+        "type": "String",
+        "initialValue": "''",
+        "key": {
+          "int": 744,
+          "string": "text"
+        },
+        "description": "String to apply as pad.",
+        "bindable": true
+      },
+      "padType": {
+        "type": "uint",
+        "initialValue": "0",
+        "key": {
+          "int": 745,
+          "string": "padtype"
+        },
+        "description": "Pad type, 0 is start, 1 is end",
+        "bindable": true
+      }
+    }
+  }
\ No newline at end of file
diff --git a/dev/defs/data_bind/converters/data_converter_string_remove_zeros.json b/dev/defs/data_bind/converters/data_converter_string_remove_zeros.json
new file mode 100644
index 0000000..5aa35c1
--- /dev/null
+++ b/dev/defs/data_bind/converters/data_converter_string_remove_zeros.json
@@ -0,0 +1,8 @@
+{
+  "name": "DataConverterStringRemoveZeros",
+  "key": {
+    "int": 531,
+    "string": "dataconverterstringremovezeros"
+  },
+  "extends": "data_bind/converters/data_converter.json"
+}
\ No newline at end of file
diff --git a/dev/defs/data_bind/converters/data_converter_string_trim.json b/dev/defs/data_bind/converters/data_converter_string_trim.json
new file mode 100644
index 0000000..481a1c6
--- /dev/null
+++ b/dev/defs/data_bind/converters/data_converter_string_trim.json
@@ -0,0 +1,20 @@
+{
+  "name": "DataConverterStringTrim",
+  "key": {
+    "int": 532,
+    "string": "dataconverterstringtrim"
+  },
+  "extends": "data_bind/converters/data_converter.json",
+  "properties": {
+    "trimType": {
+      "type": "uint",
+      "initialValue": "1",
+      "key": {
+        "int": 746,
+        "string": "trimtype"
+      },
+      "description": "Trim type, 0 is none, 1 is start, 2 is end, 3 is all",
+      "bindable": true
+    }
+  }
+}
\ No newline at end of file
diff --git a/include/rive/data_bind/converters/data_converter_string_pad.hpp b/include/rive/data_bind/converters/data_converter_string_pad.hpp
new file mode 100644
index 0000000..aae5af1
--- /dev/null
+++ b/include/rive/data_bind/converters/data_converter_string_pad.hpp
@@ -0,0 +1,20 @@
+#ifndef _RIVE_DATA_CONVERTER_STRING_PAD_HPP_
+#define _RIVE_DATA_CONVERTER_STRING_PAD_HPP_
+#include "rive/generated/data_bind/converters/data_converter_string_pad_base.hpp"
+#include "rive/data_bind/data_bind.hpp"
+#include "rive/data_bind/data_values/data_value_string.hpp"
+#include <stdio.h>
+namespace rive
+{
+class DataConverterStringPad : public DataConverterStringPadBase
+{
+public:
+    DataValue* convert(DataValue* value, DataBind* dataBind) override;
+    DataType outputType() override { return DataType::string; };
+
+private:
+    DataValueString m_output;
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/data_bind/converters/data_converter_string_remove_zeros.hpp b/include/rive/data_bind/converters/data_converter_string_remove_zeros.hpp
new file mode 100644
index 0000000..9005ede
--- /dev/null
+++ b/include/rive/data_bind/converters/data_converter_string_remove_zeros.hpp
@@ -0,0 +1,20 @@
+#ifndef _RIVE_DATA_CONVERTER_STRING_REMOVE_ZEROS_HPP_
+#define _RIVE_DATA_CONVERTER_STRING_REMOVE_ZEROS_HPP_
+#include "rive/generated/data_bind/converters/data_converter_string_remove_zeros_base.hpp"
+#include "rive/data_bind/data_bind.hpp"
+#include "rive/data_bind/data_values/data_value_string.hpp"
+#include <stdio.h>
+namespace rive
+{
+class DataConverterStringRemoveZeros : public DataConverterStringRemoveZerosBase
+{
+public:
+    DataValue* convert(DataValue* value, DataBind* dataBind) override;
+    DataType outputType() override { return DataType::string; };
+
+private:
+    DataValueString m_output;
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/data_bind/converters/data_converter_string_trim.hpp b/include/rive/data_bind/converters/data_converter_string_trim.hpp
new file mode 100644
index 0000000..912d744
--- /dev/null
+++ b/include/rive/data_bind/converters/data_converter_string_trim.hpp
@@ -0,0 +1,46 @@
+#ifndef _RIVE_DATA_CONVERTER_STRING_TRIM_HPP_
+#define _RIVE_DATA_CONVERTER_STRING_TRIM_HPP_
+#include "rive/generated/data_bind/converters/data_converter_string_trim_base.hpp"
+#include "rive/data_bind/data_bind.hpp"
+#include "rive/data_bind/data_values/data_value_string.hpp"
+#include "rive/trim_type.hpp"
+#include <stdio.h>
+#include <algorithm>
+#include <cctype>
+#include <locale>
+namespace rive
+{
+class DataConverterStringTrim : public DataConverterStringTrimBase
+{
+public:
+    DataValue* convert(DataValue* value, DataBind* dataBind) override;
+    DataType outputType() override { return DataType::string; };
+    TrimType trimValue() { return (TrimType)trimType(); }
+
+private:
+    DataValueString m_output;
+    inline void ltrim(std::string& s)
+    {
+        s.erase(s.begin(),
+                std::find_if(s.begin(), s.end(), [](unsigned char ch) {
+                    return !std::isspace(ch);
+                }));
+    }
+
+    inline void rtrim(std::string& s)
+    {
+        s.erase(std::find_if(s.rbegin(),
+                             s.rend(),
+                             [](unsigned char ch) { return !std::isspace(ch); })
+                    .base(),
+                s.end());
+    }
+    inline void trim(std::string& s)
+    {
+        rtrim(s);
+        ltrim(s);
+    }
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp
index 0a809c1..2659c01 100644
--- a/include/rive/generated/core_registry.hpp
+++ b/include/rive/generated/core_registry.hpp
@@ -138,6 +138,9 @@
 #include "rive/data_bind/converters/data_converter_operation_viewmodel.hpp"
 #include "rive/data_bind/converters/data_converter_range_mapper.hpp"
 #include "rive/data_bind/converters/data_converter_rounder.hpp"
+#include "rive/data_bind/converters/data_converter_string_pad.hpp"
+#include "rive/data_bind/converters/data_converter_string_remove_zeros.hpp"
+#include "rive/data_bind/converters/data_converter_string_trim.hpp"
 #include "rive/data_bind/converters/data_converter_system_degs_to_rads.hpp"
 #include "rive/data_bind/converters/data_converter_system_normalizer.hpp"
 #include "rive/data_bind/converters/data_converter_to_string.hpp"
@@ -549,10 +552,16 @@
                 return new DataConverterGroupItem();
             case DataConverterGroupBase::typeKey:
                 return new DataConverterGroup();
+            case DataConverterStringRemoveZerosBase::typeKey:
+                return new DataConverterStringRemoveZeros();
             case DataConverterRounderBase::typeKey:
                 return new DataConverterRounder();
+            case DataConverterStringPadBase::typeKey:
+                return new DataConverterStringPad();
             case DataConverterTriggerBase::typeKey:
                 return new DataConverterTrigger();
+            case DataConverterStringTrimBase::typeKey:
+                return new DataConverterStringTrim();
             case DataConverterOperationViewModelBase::typeKey:
                 return new DataConverterOperationViewModel();
             case DataConverterToStringBase::typeKey:
@@ -1247,6 +1256,15 @@
             case DataConverterRounderBase::decimalsPropertyKey:
                 object->as<DataConverterRounderBase>()->decimals(value);
                 break;
+            case DataConverterStringPadBase::lengthPropertyKey:
+                object->as<DataConverterStringPadBase>()->length(value);
+                break;
+            case DataConverterStringPadBase::padTypePropertyKey:
+                object->as<DataConverterStringPadBase>()->padType(value);
+                break;
+            case DataConverterStringTrimBase::trimTypePropertyKey:
+                object->as<DataConverterStringTrimBase>()->trimType(value);
+                break;
             case BindablePropertyEnumBase::propertyValuePropertyKey:
                 object->as<BindablePropertyEnumBase>()->propertyValue(value);
                 break;
@@ -1397,6 +1415,9 @@
             case DataConverterBase::namePropertyKey:
                 object->as<DataConverterBase>()->name(value);
                 break;
+            case DataConverterStringPadBase::textPropertyKey:
+                object->as<DataConverterStringPadBase>()->text(value);
+                break;
             case BindablePropertyStringBase::propertyValuePropertyKey:
                 object->as<BindablePropertyStringBase>()->propertyValue(value);
                 break;
@@ -2460,6 +2481,12 @@
                 return object->as<DataConverterGroupItemBase>()->converterId();
             case DataConverterRounderBase::decimalsPropertyKey:
                 return object->as<DataConverterRounderBase>()->decimals();
+            case DataConverterStringPadBase::lengthPropertyKey:
+                return object->as<DataConverterStringPadBase>()->length();
+            case DataConverterStringPadBase::padTypePropertyKey:
+                return object->as<DataConverterStringPadBase>()->padType();
+            case DataConverterStringTrimBase::trimTypePropertyKey:
+                return object->as<DataConverterStringTrimBase>()->trimType();
             case BindablePropertyEnumBase::propertyValuePropertyKey:
                 return object->as<BindablePropertyEnumBase>()->propertyValue();
             case NestedArtboardLeafBase::fitPropertyKey:
@@ -2570,6 +2597,8 @@
                 return object->as<OpenUrlEventBase>()->url();
             case DataConverterBase::namePropertyKey:
                 return object->as<DataConverterBase>()->name();
+            case DataConverterStringPadBase::textPropertyKey:
+                return object->as<DataConverterStringPadBase>()->text();
             case BindablePropertyStringBase::propertyValuePropertyKey:
                 return object->as<BindablePropertyStringBase>()
                     ->propertyValue();
@@ -3174,6 +3203,9 @@
             case DataConverterRangeMapperBase::flagsPropertyKey:
             case DataConverterGroupItemBase::converterIdPropertyKey:
             case DataConverterRounderBase::decimalsPropertyKey:
+            case DataConverterStringPadBase::lengthPropertyKey:
+            case DataConverterStringPadBase::padTypePropertyKey:
+            case DataConverterStringTrimBase::trimTypePropertyKey:
             case BindablePropertyEnumBase::propertyValuePropertyKey:
             case NestedArtboardLeafBase::fitPropertyKey:
             case WeightBase::valuesPropertyKey:
@@ -3222,6 +3254,7 @@
             case TransitionValueStringComparatorBase::valuePropertyKey:
             case OpenUrlEventBase::urlPropertyKey:
             case DataConverterBase::namePropertyKey:
+            case DataConverterStringPadBase::textPropertyKey:
             case BindablePropertyStringBase::propertyValuePropertyKey:
             case TextValueRunBase::textPropertyKey:
             case CustomPropertyStringBase::propertyValuePropertyKey:
@@ -3822,6 +3855,12 @@
                 return object->is<DataConverterGroupItemBase>();
             case DataConverterRounderBase::decimalsPropertyKey:
                 return object->is<DataConverterRounderBase>();
+            case DataConverterStringPadBase::lengthPropertyKey:
+                return object->is<DataConverterStringPadBase>();
+            case DataConverterStringPadBase::padTypePropertyKey:
+                return object->is<DataConverterStringPadBase>();
+            case DataConverterStringTrimBase::trimTypePropertyKey:
+                return object->is<DataConverterStringTrimBase>();
             case BindablePropertyEnumBase::propertyValuePropertyKey:
                 return object->is<BindablePropertyEnumBase>();
             case NestedArtboardLeafBase::fitPropertyKey:
@@ -3914,6 +3953,8 @@
                 return object->is<OpenUrlEventBase>();
             case DataConverterBase::namePropertyKey:
                 return object->is<DataConverterBase>();
+            case DataConverterStringPadBase::textPropertyKey:
+                return object->is<DataConverterStringPadBase>();
             case BindablePropertyStringBase::propertyValuePropertyKey:
                 return object->is<BindablePropertyStringBase>();
             case TextValueRunBase::textPropertyKey:
diff --git a/include/rive/generated/data_bind/converters/data_converter_string_pad_base.hpp b/include/rive/generated/data_bind/converters/data_converter_string_pad_base.hpp
new file mode 100644
index 0000000..175987a
--- /dev/null
+++ b/include/rive/generated/data_bind/converters/data_converter_string_pad_base.hpp
@@ -0,0 +1,109 @@
+#ifndef _RIVE_DATA_CONVERTER_STRING_PAD_BASE_HPP_
+#define _RIVE_DATA_CONVERTER_STRING_PAD_BASE_HPP_
+#include <string>
+#include "rive/core/field_types/core_string_type.hpp"
+#include "rive/core/field_types/core_uint_type.hpp"
+#include "rive/data_bind/converters/data_converter.hpp"
+namespace rive
+{
+class DataConverterStringPadBase : public DataConverter
+{
+protected:
+    typedef DataConverter Super;
+
+public:
+    static const uint16_t typeKey = 530;
+
+    /// Helper to quickly determine if a core object extends another without
+    /// RTTI at runtime.
+    bool isTypeOf(uint16_t typeKey) const override
+    {
+        switch (typeKey)
+        {
+            case DataConverterStringPadBase::typeKey:
+            case DataConverterBase::typeKey:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    uint16_t coreType() const override { return typeKey; }
+
+    static const uint16_t lengthPropertyKey = 743;
+    static const uint16_t textPropertyKey = 744;
+    static const uint16_t padTypePropertyKey = 745;
+
+protected:
+    uint32_t m_Length = 1;
+    std::string m_Text = "";
+    uint32_t m_PadType = 0;
+
+public:
+    inline uint32_t length() const { return m_Length; }
+    void length(uint32_t value)
+    {
+        if (m_Length == value)
+        {
+            return;
+        }
+        m_Length = value;
+        lengthChanged();
+    }
+
+    inline const std::string& text() const { return m_Text; }
+    void text(std::string value)
+    {
+        if (m_Text == value)
+        {
+            return;
+        }
+        m_Text = value;
+        textChanged();
+    }
+
+    inline uint32_t padType() const { return m_PadType; }
+    void padType(uint32_t value)
+    {
+        if (m_PadType == value)
+        {
+            return;
+        }
+        m_PadType = value;
+        padTypeChanged();
+    }
+
+    Core* clone() const override;
+    void copy(const DataConverterStringPadBase& object)
+    {
+        m_Length = object.m_Length;
+        m_Text = object.m_Text;
+        m_PadType = object.m_PadType;
+        DataConverter::copy(object);
+    }
+
+    bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+    {
+        switch (propertyKey)
+        {
+            case lengthPropertyKey:
+                m_Length = CoreUintType::deserialize(reader);
+                return true;
+            case textPropertyKey:
+                m_Text = CoreStringType::deserialize(reader);
+                return true;
+            case padTypePropertyKey:
+                m_PadType = CoreUintType::deserialize(reader);
+                return true;
+        }
+        return DataConverter::deserialize(propertyKey, reader);
+    }
+
+protected:
+    virtual void lengthChanged() {}
+    virtual void textChanged() {}
+    virtual void padTypeChanged() {}
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/generated/data_bind/converters/data_converter_string_remove_zeros_base.hpp b/include/rive/generated/data_bind/converters/data_converter_string_remove_zeros_base.hpp
new file mode 100644
index 0000000..076002d
--- /dev/null
+++ b/include/rive/generated/data_bind/converters/data_converter_string_remove_zeros_base.hpp
@@ -0,0 +1,36 @@
+#ifndef _RIVE_DATA_CONVERTER_STRING_REMOVE_ZEROS_BASE_HPP_
+#define _RIVE_DATA_CONVERTER_STRING_REMOVE_ZEROS_BASE_HPP_
+#include "rive/data_bind/converters/data_converter.hpp"
+namespace rive
+{
+class DataConverterStringRemoveZerosBase : public DataConverter
+{
+protected:
+    typedef DataConverter Super;
+
+public:
+    static const uint16_t typeKey = 531;
+
+    /// Helper to quickly determine if a core object extends another without
+    /// RTTI at runtime.
+    bool isTypeOf(uint16_t typeKey) const override
+    {
+        switch (typeKey)
+        {
+            case DataConverterStringRemoveZerosBase::typeKey:
+            case DataConverterBase::typeKey:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    uint16_t coreType() const override { return typeKey; }
+
+    Core* clone() const override;
+
+protected:
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/generated/data_bind/converters/data_converter_string_trim_base.hpp b/include/rive/generated/data_bind/converters/data_converter_string_trim_base.hpp
new file mode 100644
index 0000000..4441cda
--- /dev/null
+++ b/include/rive/generated/data_bind/converters/data_converter_string_trim_base.hpp
@@ -0,0 +1,71 @@
+#ifndef _RIVE_DATA_CONVERTER_STRING_TRIM_BASE_HPP_
+#define _RIVE_DATA_CONVERTER_STRING_TRIM_BASE_HPP_
+#include "rive/core/field_types/core_uint_type.hpp"
+#include "rive/data_bind/converters/data_converter.hpp"
+namespace rive
+{
+class DataConverterStringTrimBase : public DataConverter
+{
+protected:
+    typedef DataConverter Super;
+
+public:
+    static const uint16_t typeKey = 532;
+
+    /// Helper to quickly determine if a core object extends another without
+    /// RTTI at runtime.
+    bool isTypeOf(uint16_t typeKey) const override
+    {
+        switch (typeKey)
+        {
+            case DataConverterStringTrimBase::typeKey:
+            case DataConverterBase::typeKey:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    uint16_t coreType() const override { return typeKey; }
+
+    static const uint16_t trimTypePropertyKey = 746;
+
+protected:
+    uint32_t m_TrimType = 1;
+
+public:
+    inline uint32_t trimType() const { return m_TrimType; }
+    void trimType(uint32_t value)
+    {
+        if (m_TrimType == value)
+        {
+            return;
+        }
+        m_TrimType = value;
+        trimTypeChanged();
+    }
+
+    Core* clone() const override;
+    void copy(const DataConverterStringTrimBase& object)
+    {
+        m_TrimType = object.m_TrimType;
+        DataConverter::copy(object);
+    }
+
+    bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+    {
+        switch (propertyKey)
+        {
+            case trimTypePropertyKey:
+                m_TrimType = CoreUintType::deserialize(reader);
+                return true;
+        }
+        return DataConverter::deserialize(propertyKey, reader);
+    }
+
+protected:
+    virtual void trimTypeChanged() {}
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/trim_type.hpp b/include/rive/trim_type.hpp
new file mode 100644
index 0000000..ad9a068
--- /dev/null
+++ b/include/rive/trim_type.hpp
@@ -0,0 +1,13 @@
+#ifndef _RIVE_TRIM_TYPE_HPP_
+#define _RIVE_TRIM_TYPE_HPP_
+namespace rive
+{
+enum class TrimType : int
+{
+    none = 0,
+    start = 1,
+    end = 2,
+    all = 3,
+};
+}
+#endif
\ No newline at end of file
diff --git a/src/data_bind/converters/data_converter_string_pad.cpp b/src/data_bind/converters/data_converter_string_pad.cpp
new file mode 100644
index 0000000..e1dcba3
--- /dev/null
+++ b/src/data_bind/converters/data_converter_string_pad.cpp
@@ -0,0 +1,44 @@
+#include "rive/math/math_types.hpp"
+#include "rive/data_bind/converters/data_converter_string_pad.hpp"
+#include "rive/data_bind/data_values/data_value_string.hpp"
+
+using namespace rive;
+
+DataValue* DataConverterStringPad::convert(DataValue* input, DataBind* dataBind)
+{
+    if (input->is<DataValueString>())
+    {
+        auto inputValue = input->as<DataValueString>()->value();
+        auto inputLength = inputValue.size();
+        if (inputLength < length())
+        {
+            auto padPattern = text();
+            auto padLength = padPattern.size();
+            inputValue.reserve(length());
+            std::string padText{""};
+            int padTextSize = length() - inputLength;
+            padText.reserve(padTextSize);
+            while (inputLength < length())
+            {
+                auto maxLength =
+                    (padTextSize > padLength) ? padLength : padTextSize;
+                padText.append(padPattern, 0, maxLength);
+                inputLength += maxLength;
+            }
+            if (padType() == 1)
+            {
+                inputValue.append(padText, 0, padTextSize);
+            }
+            else
+            {
+                inputValue.insert(0, padText, 0, padTextSize);
+            }
+        }
+        m_output.value(inputValue);
+    }
+    else
+    {
+        m_output.value(DataValueString::defaultValue);
+    }
+    return &m_output;
+}
\ No newline at end of file
diff --git a/src/data_bind/converters/data_converter_string_remove_zeros.cpp b/src/data_bind/converters/data_converter_string_remove_zeros.cpp
new file mode 100644
index 0000000..df5c859
--- /dev/null
+++ b/src/data_bind/converters/data_converter_string_remove_zeros.cpp
@@ -0,0 +1,33 @@
+#include "rive/math/math_types.hpp"
+#include "rive/data_bind/converters/data_converter_string_remove_zeros.hpp"
+#include "rive/data_bind/data_values/data_value_string.hpp"
+
+using namespace rive;
+
+DataValue* DataConverterStringRemoveZeros::convert(DataValue* input,
+                                                   DataBind* dataBind)
+{
+    if (input->is<DataValueString>())
+    {
+        auto inputValue = input->as<DataValueString>()->value();
+
+        if (inputValue.find('.') != std::string::npos)
+        {
+            // Remove trailing zeroes
+            inputValue =
+                inputValue.substr(0, inputValue.find_last_not_of('0') + 1);
+            // If the decimal point is now the last character, remove that as
+            // well
+            if (inputValue.find('.') == inputValue.size() - 1)
+            {
+                inputValue = inputValue.substr(0, inputValue.size() - 1);
+            }
+        }
+        m_output.value(inputValue);
+    }
+    else
+    {
+        m_output.value(DataValueString::defaultValue);
+    }
+    return &m_output;
+}
\ No newline at end of file
diff --git a/src/data_bind/converters/data_converter_string_trim.cpp b/src/data_bind/converters/data_converter_string_trim.cpp
new file mode 100644
index 0000000..b2a28a8
--- /dev/null
+++ b/src/data_bind/converters/data_converter_string_trim.cpp
@@ -0,0 +1,35 @@
+#include "rive/math/math_types.hpp"
+#include "rive/data_bind/converters/data_converter_string_trim.hpp"
+#include "rive/data_bind/data_values/data_value_string.hpp"
+
+using namespace rive;
+
+DataValue* DataConverterStringTrim::convert(DataValue* input,
+                                            DataBind* dataBind)
+{
+    if (input->is<DataValueString>())
+    {
+        auto inputValue = input->as<DataValueString>()->value();
+        switch (trimValue())
+        {
+            case TrimType::start:
+                ltrim(inputValue);
+                break;
+            case TrimType::end:
+                rtrim(inputValue);
+                break;
+            case TrimType::all:
+                trim(inputValue);
+                break;
+            default:
+                break;
+        }
+
+        m_output.value(inputValue);
+    }
+    else
+    {
+        m_output.value(DataValueString::defaultValue);
+    }
+    return &m_output;
+}
\ No newline at end of file
diff --git a/src/data_bind/converters/data_converter_to_string.cpp b/src/data_bind/converters/data_converter_to_string.cpp
index 0b51a33..0fe57ff 100644
--- a/src/data_bind/converters/data_converter_to_string.cpp
+++ b/src/data_bind/converters/data_converter_to_string.cpp
@@ -12,17 +12,6 @@
     {
         float value = input->as<DataValueNumber>()->value();
         std::string str = std::to_string(value);
-        if (str.find('.') != std::string::npos)
-        {
-            // Remove trailing zeroes
-            str = str.substr(0, str.find_last_not_of('0') + 1);
-            // If the decimal point is now the last character, remove that as
-            // well
-            if (str.find('.') == str.size() - 1)
-            {
-                str = str.substr(0, str.size() - 1);
-            }
-        }
         m_output.value(str);
     }
     else if (input->is<DataValueEnum>())
diff --git a/src/generated/data_bind/converters/data_converter_string_pad_base.cpp b/src/generated/data_bind/converters/data_converter_string_pad_base.cpp
new file mode 100644
index 0000000..112cbeb
--- /dev/null
+++ b/src/generated/data_bind/converters/data_converter_string_pad_base.cpp
@@ -0,0 +1,11 @@
+#include "rive/generated/data_bind/converters/data_converter_string_pad_base.hpp"
+#include "rive/data_bind/converters/data_converter_string_pad.hpp"
+
+using namespace rive;
+
+Core* DataConverterStringPadBase::clone() const
+{
+    auto cloned = new DataConverterStringPad();
+    cloned->copy(*this);
+    return cloned;
+}
diff --git a/src/generated/data_bind/converters/data_converter_string_remove_zeros_base.cpp b/src/generated/data_bind/converters/data_converter_string_remove_zeros_base.cpp
new file mode 100644
index 0000000..9b1cb5a
--- /dev/null
+++ b/src/generated/data_bind/converters/data_converter_string_remove_zeros_base.cpp
@@ -0,0 +1,11 @@
+#include "rive/generated/data_bind/converters/data_converter_string_remove_zeros_base.hpp"
+#include "rive/data_bind/converters/data_converter_string_remove_zeros.hpp"
+
+using namespace rive;
+
+Core* DataConverterStringRemoveZerosBase::clone() const
+{
+    auto cloned = new DataConverterStringRemoveZeros();
+    cloned->copy(*this);
+    return cloned;
+}
diff --git a/src/generated/data_bind/converters/data_converter_string_trim_base.cpp b/src/generated/data_bind/converters/data_converter_string_trim_base.cpp
new file mode 100644
index 0000000..ed5c030
--- /dev/null
+++ b/src/generated/data_bind/converters/data_converter_string_trim_base.cpp
@@ -0,0 +1,11 @@
+#include "rive/generated/data_bind/converters/data_converter_string_trim_base.hpp"
+#include "rive/data_bind/converters/data_converter_string_trim.hpp"
+
+using namespace rive;
+
+Core* DataConverterStringTrimBase::clone() const
+{
+    auto cloned = new DataConverterStringTrim();
+    cloned->copy(*this);
+    return cloned;
+}