diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 55401bf0eb2211cae86503a2bb26185e499ce74d_LmdpdGxhYi1jaS55bWw=..9a2413fce7bc5e6d77de207260619f423e1f7e58_LmdpdGxhYi1jaS55bWw= 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -9,7 +9,7 @@
 
 mypy:
   variables:
-    INSTALL_SECTION: "[test,schema-validation]"
+    INSTALL_SECTION: "[test,schema-validation,qrcode]"
 
 test:
   variables:
@@ -13,7 +13,7 @@
 
 test:
   variables:
-    INSTALL_SECTION: "[test,schema-validation]"
+    INSTALL_SECTION: "[test,schema-validation,qrcode]"
 
 test-no-schema-validation:
   extends: test
diff --git a/NEWS.rst b/NEWS.rst
index 55401bf0eb2211cae86503a2bb26185e499ce74d_TkVXUy5yc3Q=..9a2413fce7bc5e6d77de207260619f423e1f7e58_TkVXUy5yc3Q= 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -1,6 +1,14 @@
 Changelog
 =========
 
+18.0.6.1.0
+----------
+
+- Image converter is copypasted from the redner Odoo Module. long term, it must
+  be deleted from the redner Module
+
+- Add QR code converter & test it
+
 18.0.6.0.0
 ----------
 
diff --git a/__init__.py b/__init__.py
index 55401bf0eb2211cae86503a2bb26185e499ce74d_X19pbml0X18ucHk=..9a2413fce7bc5e6d77de207260619f423e1f7e58_X19pbml0X18ucHk= 100644
--- a/__init__.py
+++ b/__init__.py
@@ -53,3 +53,5 @@
     Validator,
 )
 from .xref import Xref, JsonLD_ID
+from .image import ImageFile, ImageDataURL
+from .qr_code import QRCodeFile, QRCodeDataURL, stringToQRCode, QRToImage
diff --git a/__manifest__.py b/__manifest__.py
index 55401bf0eb2211cae86503a2bb26185e499ce74d_X19tYW5pZmVzdF9fLnB5..9a2413fce7bc5e6d77de207260619f423e1f7e58_X19tYW5pZmVzdF9fLnB5 100644
--- a/__manifest__.py
+++ b/__manifest__.py
@@ -21,7 +21,7 @@
     "name": "Converter",
     "license": "AGPL-3",
     "summary": "Convert odoo records to/from plain data structures.",
-    "version": "18.0.6.0.0",
+    "version": "18.0.6.1.0",
     "category": "Hidden",
     "author": "XCG Consulting",
     "website": "https://orbeet.io/",
diff --git a/image.py b/image.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a2413fce7bc5e6d77de207260619f423e1f7e58_aW1hZ2UucHk=
--- /dev/null
+++ b/image.py
@@ -0,0 +1,66 @@
+##############################################################################
+#
+#    Converter Odoo module
+#    Copyright © 2020, 2025 XCG Consulting <https://xcg-consulting.fr>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import base64
+from collections.abc import Mapping
+from typing import Any
+
+from odoo import models  # type: ignore[import-untyped]
+from odoo.tools.mimetypes import guess_mimetype  # type: ignore[import-untyped]
+
+from .base import Converter
+
+
+def image(value: bytes):
+    # get MIME type associated with the decoded_data.
+    image_base64 = base64.b64decode(value)
+    mimetype = guess_mimetype(image_base64)
+    return {"body": value.decode("ascii"), "mime-type": mimetype}
+
+
+class ImageFile(Converter):
+    def __init__(self, fieldname):
+        self.fieldname = fieldname
+
+    def odoo_to_message(
+        self, instance: models.Model, ctx: Mapping | None = None
+    ) -> Any:
+        value = getattr(instance, self.fieldname)
+
+        if not value:
+            return {}
+
+        return image(value)
+
+
+class ImageDataURL(Converter):
+    def __init__(self, fieldname):
+        self.fieldname = fieldname
+
+    def odoo_to_message(
+        self, instance: models.Model, ctx: Mapping | None = None
+    ) -> Any:
+        value = getattr(instance, self.fieldname)
+
+        if not value:
+            return ""
+        tmp = image(value)
+
+        return "data:{};base64,{}".format(tmp["mime-type"], tmp["body"])
diff --git a/pyproject.toml b/pyproject.toml
index 55401bf0eb2211cae86503a2bb26185e499ce74d_cHlwcm9qZWN0LnRvbWw=..9a2413fce7bc5e6d77de207260619f423e1f7e58_cHlwcm9qZWN0LnRvbWw= 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,6 +19,7 @@
 
 [project.optional-dependencies]
 schema-validation = ["fastjsonschema"]
+qrcode = ["qrcode"]
 doc = ["sphinx"]
 test = []
 
diff --git a/qr_code.py b/qr_code.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a2413fce7bc5e6d77de207260619f423e1f7e58_cXJfY29kZS5weQ==
--- /dev/null
+++ b/qr_code.py
@@ -0,0 +1,97 @@
+##############################################################################
+#
+#    Converter Odoo module
+#    Copyright © 2020, 2025 XCG Consulting <https://xcg-consulting.fr>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import base64
+import io
+from collections.abc import Mapping
+from typing import Any
+
+from odoo import models  # type: ignore[import-untyped]
+
+from .base import Converter
+
+_qrcode: None | Exception = None
+try:
+    import qrcode  # type: ignore[import-untyped]
+except ImportError as e:
+    _qrcode = e
+
+
+def QRToImage(qr):
+    """Generate a image dict from a QR Code"""
+    img = qr.make_image(fill="black", back_color="white")
+    buffered = io.BytesIO()
+    img.save(buffered, format="PNG")
+
+    body = base64.b64encode(buffered.getvalue()).decode("ascii")
+    return {"body": body, "mime-type": "image/png"}
+
+
+def stringToQRCode(data: str):
+    """Generate a QR code from a string and return it as a base64-encoded image."""
+    if not data:
+        return False
+
+    if _qrcode is not None:
+        raise _qrcode
+
+    qr = qrcode.QRCode(
+        version=1,
+        error_correction=qrcode.constants.ERROR_CORRECT_L,
+        box_size=10,
+        border=4,
+    )
+    qr.add_data(data)
+    qr.make(fit=True)
+    return qr
+
+
+class QRCodeFile(Converter):
+    def __init__(self, fieldname):
+        self.fieldname = fieldname
+
+    def odoo_to_message(
+        self, instance: models.Model, ctx: Mapping | None = None
+    ) -> Any:
+        value = getattr(instance, self.fieldname)
+
+        if not value:
+            return {}
+
+        return QRToImage(stringToQRCode(value))
+
+
+class QRCodeDataURL(Converter):
+    def __init__(self, fieldname):
+        self.fieldname = fieldname
+
+    def odoo_to_message(
+        self, instance: models.Model, ctx: Mapping | None = None
+    ) -> Any:
+        value = getattr(instance, self.fieldname)
+
+        if not value:
+            return ""
+
+        image = QRToImage(stringToQRCode(value))
+        return "data:{};base64,{}".format(
+            image["mime-type"],
+            image["body"],
+        )
diff --git a/tests/__init__.py b/tests/__init__.py
index 55401bf0eb2211cae86503a2bb26185e499ce74d_dGVzdHMvX19pbml0X18ucHk=..9a2413fce7bc5e6d77de207260619f423e1f7e58_dGVzdHMvX19pbml0X18ucHk= 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -4,6 +4,7 @@
     test_field,
     test_ir_model,
     test_mail_template,
+    test_qr_code,
     test_relation,
     test_switch,
     test_validate,
diff --git a/tests/test_qr_code.py b/tests/test_qr_code.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a2413fce7bc5e6d77de207260619f423e1f7e58_dGVzdHMvdGVzdF9xcl9jb2RlLnB5
--- /dev/null
+++ b/tests/test_qr_code.py
@@ -0,0 +1,62 @@
+##############################################################################
+#
+#    Converter Odoo module
+#    Copyright © 2025 XCG Consulting <https://xcg-consulting.fr>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from unittest import skipUnless
+
+from odoo import tests  # type: ignore[import-untyped]
+
+from .. import QRCodeDataURL, QRCodeFile, stringToQRCode
+
+
+@skipUnless(tests.can_import("qrcode"), "qrcode module not available")
+class TestQRCode(tests.TransactionCase):
+    def setUp(self):
+        super().setUp()
+        self.ready_mat = self.env["res.partner"].search(
+            [("name", "=", "Ready Mat")], limit=1
+        )
+
+    def test_print_qr_code_1(self):
+        qr = stringToQRCode("https://google.com")
+        qr.print_ascii()
+
+    def test_string_to_qr_code_2(self):
+        qr = stringToQRCode(self.ready_mat.website)
+        qr.print_ascii()
+
+    def test_qr_code_file(self):
+        converter = QRCodeFile("website")
+        img = converter.odoo_to_message(self.ready_mat)
+
+        self.assertEqual("image/png", img["mime-type"])
+        self.assertRegex(
+            img["body"],
+            r"[A-Za-z0-9+/=]+$",
+            "The generated base64 body is incorrect",
+        )
+
+    def test_qr_code_data_url(self):
+        converter = QRCodeDataURL("website")
+        url = converter.odoo_to_message(self.ready_mat)
+        self.assertRegex(
+            url,
+            r"^data:image/png;base64,[A-Za-z0-9+/=]+$",
+            "The generated Data URL is not correctly formatted",
+        )