Repository: Pavkazzz/pyzint (language: python)

Issue:
Cyrillic locale is affecting svg generation
When using an locale where decimal separator is differs from dot "." function render_svg returns an  incorrect svg image.

Example
Correct rect-row
<rect x="32.00" y="0.00" width="2.00" height="4.00" />
Incorrect rect-row (ru_RU locale is used)
<rect x="32,00" y="0,00" width="2,00" height="4,00" />

Steps to reproduce

`from pyzint import zint`
`import locale`
`locale.setlocale(locale.LC_NUMERIC,"ru_RU.utf8")`
`print(zint.Zint("test", zint.BARCODE_AZTEC, height=200, border_width=4).render_svg().decode())`






Discussion / hints:

Fix patch:
diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml
index 3db9ca4..6e0b1af 100644
--- a/.github/workflows/python-publish.yml
+++ b/.github/workflows/python-publish.yml
@@ -27,7 +27,7 @@ jobs:
     - name: build manylinux wheels
       uses: RalfG/python-wheels-manylinux-build@v0.3.1
       with:
-        python-versions: 'cp35-cp35m cp36-cp36m cp37-cp37m cp38-cp38 cp39-cp39'
+        python-versions: 'cp35-cp35m cp36-cp36m cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310'
 
     - name: Publish
       env:
@@ -66,7 +66,7 @@ jobs:
     runs-on: windows-latest
     strategy:
       matrix:
-        python-version: [3.5, 3.6, 3.7, 3.8, 3.9]
+        python-version: [3.5, 3.6, 3.7, 3.8, 3.9, "3.10"]
 
     steps:
     - uses: actions/checkout@v2
@@ -93,7 +93,7 @@ jobs:
     runs-on: macos-latest
     strategy:
       matrix:
-        python-version: [3.5, 3.6, 3.7, 3.8, 3.9]
+        python-version: [3.5, 3.6, 3.7, 3.8, 3.9, "3.10"]
 
     steps:
     - uses: actions/checkout@v2
diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index 10805c5..01163dd 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -11,11 +11,10 @@ on:
 
 jobs:
   build:
-
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: [3.5, 3.6, 3.7, 3.8]
+        python-version: [3.5, 3.6, 3.7, 3.8, 3.9]
 
     steps:
     - uses: actions/checkout@v2
@@ -27,12 +26,16 @@ jobs:
         python-version: ${{ matrix.python-version }}
     - name: Install dependencies
       run: |
-        python -m pip install --upgrade pip
+        python -m pip install --upgrade pip wheel numpy
         pip install -r requirements.dev.txt
         pip install -e .
     - name: Lint with flake8
       run: |
         pylama pyzint tests
+    - name: Install locale
+      run: |
+        sudo locale-gen ru_RU
+        sudo locale-gen ru_RU.UTF-8
     - name: Test with pytest
       run: |
         pytest
diff --git a/.github/workflows/pythonpackage310.yml b/.github/workflows/pythonpackage310.yml
new file mode 100644
index 0000000..abe3535
--- /dev/null
+++ b/.github/workflows/pythonpackage310.yml
@@ -0,0 +1,41 @@
+# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
+# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
+
+name: Python package
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: ["3.10"]
+
+    steps:
+    - uses: actions/checkout@v2
+      with:
+        submodules: true
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v1
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip wheel
+        pip install -r requirements.dev.310plus.txt
+        pip install -e .
+    - name: Lint with flake8
+      run: |
+        pylama pyzint tests
+    - name: Install locale
+      run: |
+        sudo locale-gen ru_RU
+        sudo locale-gen ru_RU.UTF-8
+    - name: Test with pytest
+      run: |
+        pytest
diff --git a/pyzint/zint.c b/pyzint/zint.c
index 973e7f6..3224fcb 100644
--- a/pyzint/zint.c
+++ b/pyzint/zint.c
@@ -4,6 +4,8 @@
 #include "src/zint/backend/gb18030.h"
 
 #define PY_SSIZE_T_CLEAN
+#define PYZINT_FLOAT_FMT_ALLOC 50
+
 #include <Python.h>
 #include <structmember.h>
 
@@ -691,6 +693,19 @@ static PyObject* CZINT_render_bmp(
     return result;
 }
 
+
+void formatFloat(double num, int precision, char* res) {
+  long precision_mul = 1;
+  double integral;
+  double fractional;
+
+  for (int i = 0; i < precision; i++) precision_mul *= 10;
+  long intFrac = (long)(modf(num, &integral) * precision_mul);
+  long intIntegral = (long)integral;
+
+  sprintf(res, "%ld.%ld", intIntegral, intFrac);
+}
+
 PyDoc_STRVAR(CZINT_render_svg_docstring,
     "Render svg barcode.\n\n"
     "    Zint('data', BARCODE_QRCODE).render_svg(angle: int = 0, fgcolor: str = '#FFFFFF', bgcolor: str = '#000000') -> bytes"
@@ -794,7 +809,17 @@ static PyObject* CZINT_render_svg(
         len_fsvg += snprintf(&fsvg[len_fsvg], max_len, "<rect x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" fill=\"#%s\" />\n", (int) ceil(symbol->vector->width), (int) ceil(symbol->vector->height), symbol->bgcolour);
         rect = symbol->vector->rectangles;
         while (rect) {
-            len_fsvg += snprintf(&fsvg[len_fsvg], max_len-len_fsvg, "<rect x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\" />\n", rect->x, rect->y, rect->width, rect->height);
+            char buff_x[PYZINT_FLOAT_FMT_ALLOC] = {0};
+            char buff_y[PYZINT_FLOAT_FMT_ALLOC] = {0};
+            char buff_width[PYZINT_FLOAT_FMT_ALLOC] = {0};
+            char buff_height[PYZINT_FLOAT_FMT_ALLOC] = {0};
+
+            formatFloat(rect->x, 2, &buff_x);
+            formatFloat(rect->y, 2, &buff_y);
+            formatFloat(rect->width, 2, &buff_width);
+            formatFloat(rect->height, 2, &buff_height);
+
+            len_fsvg += snprintf(&fsvg[len_fsvg], max_len-len_fsvg, "<rect x=\"%s\" y=\"%s\" width=\"%s\" height=\"%s\" />\n", buff_x, buff_y, buff_width, buff_height);
             rect = rect->next;
         }
 
diff --git a/requirements.dev.310plus.txt b/requirements.dev.310plus.txt
new file mode 100644
index 0000000..3913948
--- /dev/null
+++ b/requirements.dev.310plus.txt
@@ -0,0 +1,7 @@
+numpy
+Pillow~=8.4.0
+pylama
+pytest~=6.2.4
+scikit-image==0.19.0
+wheel
+zbar-py~=1.0.4
diff --git a/requirements.dev.txt b/requirements.dev.txt
index 628f82b..26188a0 100644
--- a/requirements.dev.txt
+++ b/requirements.dev.txt
@@ -1,6 +1,7 @@
-pytest~=5.4.1
-pylama
+numpy
 Pillow==7.1.0
+pylama
+pytest~=5.4.1
+scikit-image
 wheel
 zbar-py~=1.0.4
-scikit-image
diff --git a/setup.py b/setup.py
index 68c19f7..e209c35 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@
 
 setup(
     name="pyzint",
-    version="0.1.8",
+    version="0.1.9",
     description="Python ZINT bindings",
     long_description=open("README.rst").read(),
     packages=["pyzint"],


Test patch:
diff --git a/tests/test_locale.py b/tests/test_locale.py
new file mode 100644
index 0000000..c51156d
--- /dev/null
+++ b/tests/test_locale.py
@@ -0,0 +1,9 @@
+import locale
+
+from pyzint import zint
+
+
+def test_ru_locale_svg():
+    locale.setlocale(locale.LC_NUMERIC, "ru_RU.utf8")
+    barcode = zint.Zint("test", zint.BARCODE_AZTEC, height=200, border_width=4)
+    assert "," not in barcode.render_svg().decode()