New Upstream Release - python-svglib

Ready changes

Summary

Merged new upstream version: 1.5.1+dfsg (was: 1.4.1+dfsg).

Resulting package

Built on 2023-02-08T02:15 (took 3m7s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases python3-svglib

Lintian Result

Diff

diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..1dcb0ac
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,13 @@
+If you want to start a discussion rather then open an issue, please use the
+now enabled GitHub [discussions feature for this project](https://github.com/deeplook/svglib/discussions)!
+
+When submitting a new issue please try to be as concise and precise as possible.
+This means to show only minimal SVG or PDF files that show something unexpected.
+
+In times of widely available files infected with malicious code on the net it
+is also appreciated to include bitmap snapshots of your PDFs inside an issue
+description or SVG code on a separate, linked GitHub gist.
+
+Otherwise please provide information where relevant like specific versions of
+components in your setup, e.g. OS, Python, reportlab, svglib, type of install
+or anything else.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..6164747
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,44 @@
+name: CI
+on: [push, pull_request, workflow_dispatch]
+jobs:
+  tests:
+    name: "Python ${{ matrix.python-version }} on ${{ matrix.os }}"
+    runs-on: "${{ matrix.os }}"
+    strategy:
+      matrix:
+        python-version: ["3.7", "3.8", "3.9", "3.10", "pypy-3.7"]
+        os: ["ubuntu-latest", "macos-latest", "windows-latest"]
+    steps:
+      - uses: "actions/checkout@v3"
+      - name: "Cache for wikipedia flags"
+        uses: actions/cache@v3
+        env:
+          cache-name: "cache-wikipedia-flags"
+        with:
+          path: "tests/samples/wikipedia/flags"
+          key: "wikipedia-flags-${{ matrix.python-version }}-${{ matrix.os }}"
+      - name: "Cache for wikipedia symbols"
+        uses: actions/cache@v3
+        env:
+          cache-name: "cache-wikipedia-symbols"
+        with:
+          path: "tests/samples/wikipedia/symbols"
+          key: "wikipedia-symbols-${{ matrix.python-version }}-${{ matrix.os }}"
+      - name: "Cache for w3c svg12 tinytestsuite"
+        uses: actions/cache@v3
+        env:
+          cache-name: "cache-w3c-svg12-tinytestsuite"
+        with:
+          path: "tests/samples/W3C_SVG_12_TinyTestSuite"
+          key: "w3c-svg12-tinytestsuite-${{ matrix.python-version }}-${{ matrix.os }}"
+      - uses: "actions/setup-python@v4"
+        with:
+          python-version: "${{ matrix.python-version }}"
+      - name: "Install dependencies"
+        run: |
+          python -VV
+          python -m site
+          python -m pip install --upgrade pip setuptools wheel
+          python -m pip install --upgrade virtualenv tox tox-gh-actions
+      - name: "Run tox targets for ${{ matrix.python-version }}"
+        run: "python -m tox"
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 0000000..4085a60
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,50 @@
+name: "CodeQL"
+
+on:
+  push:
+    branches: [ "master" ]
+  pull_request:
+    branches: [ "master" ]
+  schedule:
+    - cron: "40 23 * * 4"
+
+jobs:
+  analyze:
+    name: Analyze
+    runs-on: ubuntu-latest
+    permissions:
+      actions: read
+      contents: read
+      security-events: write
+
+    strategy:
+      fail-fast: false
+      matrix:
+        language: [ python ]
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v3
+
+      - name: After Prepare
+        run: python3 -m pip install --upgrade --user flake8
+
+      - name: Before Index
+        run: |
+          python3 -m flake8 --version
+          python3 -m flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
+          python3 -m flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
+
+      - name: Initialize CodeQL
+        uses: github/codeql-action/init@v2
+        with:
+          languages: ${{ matrix.language }}
+          queries: +security-and-quality
+
+      - name: Autobuild
+        uses: github/codeql-action/autobuild@v2
+
+      - name: Perform CodeQL Analysis
+        uses: github/codeql-action/analyze@v2
+        with:
+          category: "/language:${{ matrix.language }}"
diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml
new file mode 100644
index 0000000..b171c8b
--- /dev/null
+++ b/.github/workflows/greetings.yml
@@ -0,0 +1,16 @@
+name: Greetings
+
+on: [pull_request, issues]
+
+jobs:
+  greeting:
+    runs-on: ubuntu-latest
+    permissions:
+      issues: write
+      pull-requests: write
+    steps:
+    - uses: actions/first-interaction@v1
+      with:
+        repo-token: ${{ secrets.GITHUB_TOKEN }}
+        issue-message: 'Thank you for raising your first issue! Your help to improve svglib is much appreciated!'
+        pr-message: 'Thank you for making your first pull request! Your contribution to svglib is highly appreciated!'
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bf2b547
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,24 @@
+.cache
+.coverage
+MANIFEST
+coverage.xml
+nosetests.xml
+junit-report.xml
+pylint.txt
+toy.py
+violations.pyflakes.txt
+cover/
+build/
+docs/_build
+*.egg-info/
+*.pyc
+*.swp
+*.egg
+env/
+.workon
+dist/
+tests/samples/misc/*-svglib.pdf
+tests/samples/wikipedia/flags
+tests/samples/wikipedia/symbols
+tests/samples/W3C_SVG_12_TinyTestSuite.tar.gz
+tests/samples/W3C_SVG_12_TinyTestSuite/
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 3ef70a1..5f4b9f1 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -3,6 +3,18 @@
 ChangeLog
 =========
 
+1.5.1 (2023-01-07)
+------------------
+- Final fix to conversion from shorthand quadratic to cubic bézier (#372).
+
+1.5.0 (2023-01-05)
+------------------
+- Add support for ``ex`` units (assuming for em/2).
+- Add support for ``image/jpg`` embedded images (in addition to ``image/jpeg``).
+- Avoid crash on ``@import`` rules in stylesheets. The rules are simply ignored
+  (#285).
+- Fix conversion from shorthand quadratic to cubic bézier (#364).
+
 1.4.1 (2022-08-05)
 ------------------
 - No source code changes, only fixed a leftover set_trace() in the released code
diff --git a/README.rst b/README.rst
index 6b29304..86cbaf3 100644
--- a/README.rst
+++ b/README.rst
@@ -83,8 +83,8 @@ Features
 Known limitations
 -----------------
 
-- support for stylesheets is still experimental. Please report any
-  bug or shortcoming on the `svglib issue tracker`_.
+- @import rules in stylesheets are ignored. CSS is supported, but the range
+  of supported attributes is still limited
 - clipping is limited to single paths, no mask support
 - color gradients are not supported (limitation of reportlab)
 - SVG ``ForeignObject`` elements are not supported.
diff --git a/debian/changelog b/debian/changelog
index 3f61a3e..d3d9254 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+python-svglib (1.5.1+dfsg-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Wed, 08 Feb 2023 02:12:35 -0000
+
 python-svglib (1.4.1+dfsg-1) unstable; urgency=medium
 
   * New upstream version 1.4.1+dfsg
diff --git a/setup.cfg b/setup.cfg
index 96689e6..1b44118 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = svglib
-version = 1.4.1
+version = 1.5.1
 description = A pure-Python library for reading and converting SVG
 long_description = file: README.rst
 keywords =
diff --git a/svglib/svglib.py b/svglib/svglib.py
index 026e7d5..727b669 100755
--- a/svglib/svglib.py
+++ b/svglib/svglib.py
@@ -71,10 +71,10 @@ def find_font(font_name, weight='normal', style='normal'):
     return _fonts_find_font(font_name, weight, style)
 
 
-__version__ = '1.4.1'
+__version__ = '1.5.1'
 __license__ = 'LGPL 3'
 __author__ = 'Dinu Gherman'
-__date__ = '2022-08-05'
+__date__ = '2023-01-07'
 
 XML_NS = 'http://www.w3.org/XML/1998/namespace'
 
@@ -133,7 +133,7 @@ class CSSMatcher(cssselect2.Matcher):
         )
 
         for rule in rules:
-            if not rule.prelude:
+            if not rule.prelude or rule.type == 'at-rule':
                 continue
             selectors = cssselect2.compile_selector_list(rule.prelude)
             selector_string = tinycss2.serialize(rule.prelude)
@@ -325,10 +325,14 @@ class Svg2RlgAttributeConverter(AttributeConverter):
             return float(text[:-2]) * em_base
         elif text.endswith("px"):
             return float(text[:-2])
-
-        if "ex" in text:
-            logger.warning("Ignoring unit ex")
-            text = text.replace("ex", '')
+        elif text.endswith("ex"):
+            # The x-height of the text must be assumed to be 0.5em tall when the
+            # text cannot be measured.
+            return float(text[:-2]) * em_base / 2
+        elif text.endswith("ch"):
+            # The advance measure of the "0" glyph must be assumed to be 0.5em
+            # wide when the text cannot be measured.
+            return float(text[:-2]) * em_base / 2
 
         text = text.strip()
         length = toLength(text)  # this does the default measurements such as mm and cm
@@ -681,7 +685,7 @@ class SvgRenderer:
             return None
 
         # First handle any raster embedded image data
-        match = re.match(r"^data:image/(jpeg|png);base64", xlink_href)
+        match = re.match(r"^data:image/(jpe?g|png);base64", xlink_href)
         if match:
             img_format = match.groups()[0]
             image_data = base64.decodebytes(xlink_href[(match.span(0)[1] + 1):].encode('ascii'))
@@ -1090,6 +1094,7 @@ class Svg2RlgShapeConverter(SvgShapeConverter):
         unclosed_subpath_pointers = []
         subpath_start = []
         lastop = ''
+        last_quadratic_cp = None
 
         for i in range(0, len(normPath), 2):
             op, nums = normPath[i:i+2]
@@ -1164,15 +1169,18 @@ class Svg2RlgShapeConverter(SvgShapeConverter):
             elif op == 'Q':
                 x0, y0 = points[-2:]
                 x1, y1, xn, yn = nums
+                last_quadratic_cp = (x1, y1)
                 (x0, y0), (x1, y1), (x2, y2), (xn, yn) = \
                     convert_quadratic_to_cubic_path((x0, y0), (x1, y1), (xn, yn))
                 path.curveTo(x1, y1, x2, y2, xn, yn)
             elif op == 'T':
-                if len(points) < 4:
-                    xp, yp, x0, y0 = points[-2:] * 2
+                if last_quadratic_cp is not None:
+                    xp, yp = last_quadratic_cp
                 else:
-                    xp, yp, x0, y0 = points[-4:]
+                    xp, yp = points[-2:]
+                x0, y0 = points[-2:]
                 xi, yi = x0 + (x0 - xp), y0 + (y0 - yp)
+                last_quadratic_cp = (xi, yi)
                 xn, yn = nums
                 (x0, y0), (x1, y1), (x2, y2), (xn, yn) = \
                     convert_quadratic_to_cubic_path((x0, y0), (xi, yi), (xn, yn))
@@ -1183,18 +1191,20 @@ class Svg2RlgShapeConverter(SvgShapeConverter):
                 x0, y0 = points[-2:]
                 x1, y1, xn, yn = nums
                 x1, y1, xn, yn = x0 + x1, y0 + y1, x0 + xn, y0 + yn
+                last_quadratic_cp = (x1, y1)
                 (x0, y0), (x1, y1), (x2, y2), (xn, yn) = \
                     convert_quadratic_to_cubic_path((x0, y0), (x1, y1), (xn, yn))
                 path.curveTo(x1, y1, x2, y2, xn, yn)
             elif op == 't':
-                if len(points) < 4:
-                    xp, yp, x0, y0 = points[-2:] * 2
+                if last_quadratic_cp is not None:
+                    xp, yp = last_quadratic_cp
                 else:
-                    xp, yp, x0, y0 = points[-4:]
+                    xp, yp = points[-2:]
                 x0, y0 = points[-2:]
                 xn, yn = nums
                 xn, yn = x0 + xn, y0 + yn
                 xi, yi = x0 + (x0 - xp), y0 + (y0 - yp)
+                last_quadratic_cp = (xi, yi)
                 (x0, y0), (x1, y1), (x2, y2), (xn, yn) = \
                     convert_quadratic_to_cubic_path((x0, y0), (xi, yi), (xn, yn))
                 path.curveTo(x1, y1, x2, y2, xn, yn)
@@ -1219,6 +1229,9 @@ class Svg2RlgShapeConverter(SvgShapeConverter):
 
             else:
                 logger.debug("Suspicious path operator: %s", op)
+
+            if op not in ('Q', 'q', 'T', 't'):
+                last_quadratic_cp = None
             lastop = op
 
         gr = Group()
diff --git a/tests/test_basic.py b/tests/test_basic.py
index 429f754..bc3d319 100755
--- a/tests/test_basic.py
+++ b/tests/test_basic.py
@@ -566,6 +566,18 @@ class TestStyleSheets:
         assert main_group.contents[0].contents[1].contents[0].fontName == 'Helvetica-Bold'
         assert main_group.contents[0].contents[2].contents[0].fontName == 'Helvetica'
 
+    def test_import_rule_no_crash(self):
+        # Just test that svglib does not crash. Import rules are currently ignored.
+        drawing = drawing_from_svg('''
+            <svg>
+              <defs>
+                <style type="text/css">
+                  @import url('https://fonts.example.org/css2?family=Ubuntu+Condensed');
+                </style>
+              </defs>
+            </svg>
+        ''')
+
 
 class TestGroupNode:
     def test_svg_groups_have_svgid(self):

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/lib/python3/dist-packages/svglib-1.5.1.egg-info/PKG-INFO
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/svglib-1.5.1.egg-info/dependency_links.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/svglib-1.5.1.egg-info/requires.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/svglib-1.5.1.egg-info/top_level.txt

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/lib/python3/dist-packages/svglib-1.4.1.egg-info/PKG-INFO
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/svglib-1.4.1.egg-info/dependency_links.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/svglib-1.4.1.egg-info/requires.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/svglib-1.4.1.egg-info/top_level.txt

No differences were encountered in the control files

More details

Full run details