Repository: claudep/swiss-qr-bill

Issue:
Scissors missing on top seperation line
Currently the scissor is only shown on the vertical seperation line. However; in the standard it says that a scissor symbol should appear on both of those lines. 

I tried adding it to the top line, but my attempt doesn't look great:

```python
grp.add(dwg.text(
                "✂", insert=(add_mm(60, mm(-1.5)), 10),
                font_size=16, font_family='Helvetica'
            ))
```

Possibly you could come up with a better way and help me out?

Discussion:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index d2906f1..ab31a95 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,9 @@ ChangeLog
 Unreleased
 ----------
 - Replaced ``##`` with ``//`` as separator in additional informations (#75).
+- Print scissors symbol on horizontal separation line when not in full page.
+  WARNING: the resulting bill is 1 millimiter higher to be able to show the
+  entire symbol (#65).
 
 0.7.1 (2022-03-07)
 ------------------
diff --git a/qrbill/bill.py b/qrbill/bill.py
index f9476e8..889eeda 100644
--- a/qrbill/bill.py
+++ b/qrbill/bill.py
@@ -18,7 +18,7 @@ AMOUNT_REGEX = r'^\d{1,9}\.\d{2}$'
 DATE_REGEX = r'(\d{4})-(\d{2})-(\d{2})'
 
 MM_TO_UU = 3.543307
-BILL_HEIGHT = '105mm'
+BILL_HEIGHT = 106  # 105mm + 1mm for horizontal scissors to show up.
 RECEIPT_WIDTH = '62mm'
 PAYMENT_WIDTH = '148mm'
 MAX_CHARS_PAYMENT_LINE = 72
@@ -471,12 +471,12 @@ class QRBill:
             )
         else:
             dwg = svgwrite.Drawing(
-                size=(A4[0], BILL_HEIGHT),  # A4 width, A6 height.
+                size=(A4[0], f'{BILL_HEIGHT}mm'),  # A4 width, A6 height.
                 viewBox=('0 0 %f %f' % (mm(A4[0]), mm(BILL_HEIGHT))),
             )
         dwg.add(dwg.rect(insert=(0, 0), size=('100%', '100%'), fill='white'))  # Force white background
 
-        bill_group = self.draw_bill(dwg)
+        bill_group = self.draw_bill(dwg, horiz_scissors=not full_page)
         if full_page:
             self.transform_to_full_page(dwg, bill_group)
 
@@ -498,7 +498,7 @@ class QRBill:
 
         # add text snippet
         x_center = mm(A4[0]) / 2
-        y_pos = y_offset - mm(2)
+        y_pos = y_offset - mm(1)
 
         dwg.add(dwg.text(
             self.label("Separate before paying in"),
@@ -508,19 +508,20 @@ class QRBill:
             **self.font_info)
         )
 
-    def draw_bill(self, dwg):
+    def draw_bill(self, dwg, horiz_scissors=True):
         """Draw the bill in SVG format."""
         margin = mm(5)
         payment_left = add_mm(RECEIPT_WIDTH, margin)
         payment_detail_left = add_mm(payment_left, mm(46 + 5))
-        currency_top = mm(72)
+        above_padding = 1  # 1mm added for scissors display
+        currency_top = mm(72 + above_padding)
 
         grp = dwg.add(dwg.g())
         # Receipt
-        y_pos = 15
+        y_pos = 15 + above_padding
         line_space = 3.5
         receipt_head_font = self.head_font_info(part='receipt')
-        grp.add(dwg.text(self.label("Receipt"), (margin, mm(10)), **self.title_font_info))
+        grp.add(dwg.text(self.label("Receipt"), (margin, mm(y_pos - 5)), **self.title_font_info))
         grp.add(dwg.text(self.label("Account / Payable to"), (margin, mm(y_pos)), **receipt_head_font))
         y_pos += line_space
         grp.add(dwg.text(
@@ -572,21 +573,32 @@ class QRBill:
 
         # Right-aligned
         grp.add(dwg.text(
-            self.label("Acceptance point"), (add_mm(RECEIPT_WIDTH, margin * -1), mm(86)),
+            self.label("Acceptance point"), (add_mm(RECEIPT_WIDTH, margin * -1), mm(86 + above_padding)),
             text_anchor='end', **receipt_head_font
         ))
 
         # Top separation line
         if self.top_line:
             grp.add(dwg.line(
-                start=(0, mm(0.141)), end=(add_mm(RECEIPT_WIDTH, PAYMENT_WIDTH), mm(0.141)),
+                start=(0, mm(0.141 + above_padding)),
+                end=(add_mm(RECEIPT_WIDTH, PAYMENT_WIDTH), mm(0.141 + above_padding)),
                 stroke='black', stroke_dasharray='2 2', fill='none'
             ))
+            if horiz_scissors:
+                # Scissors on horizontal line
+                path = dwg.path(
+                    d=SCISSORS_SVG_PATH,
+                    style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none",
+                )
+                path.scale(1.9)
+                path.translate(tx=24, ty=0)
+                grp.add(path)
 
         # Separation line between receipt and payment parts
         if self.payment_line:
             grp.add(dwg.line(
-                start=(mm(RECEIPT_WIDTH), 0), end=(mm(RECEIPT_WIDTH), mm(BILL_HEIGHT)),
+                start=(mm(RECEIPT_WIDTH), mm(above_padding)),
+                end=(mm(RECEIPT_WIDTH), mm(BILL_HEIGHT - above_padding)),
                 stroke='black', stroke_dasharray='2 2', fill='none'
             ))
             # Scissors on vertical line
@@ -601,7 +613,7 @@ class QRBill:
 
         # Payment part
         payment_head_font = self.head_font_info(part='payment')
-        grp.add(dwg.text(self.label("Payment part"), (payment_left, mm(10)), **self.title_font_info))
+        grp.add(dwg.text(self.label("Payment part"), (payment_left, mm(10 + above_padding)), **self.title_font_info))
 
         # Get QR code SVG from qrcode lib, read it and redraw path in svgwrite drawing.
         buff = BytesIO()
@@ -623,16 +635,16 @@ class QRBill:
         scale_factor = mm(45.8) / im.width
 
         qr_left = payment_left
-        qr_top = 60
+        qr_top = 60 + above_padding
         path.translate(tx=qr_left, ty=qr_top)
         path.scale(scale_factor)
         grp.add(path)
 
-        self.draw_swiss_cross(dwg, grp, (payment_left, 60), im.width * scale_factor)
+        self.draw_swiss_cross(dwg, grp, (payment_left, qr_top), im.width * scale_factor)
 
         grp.add(dwg.text(self.label("Currency"), (payment_left, currency_top), **payment_head_font))
         grp.add(dwg.text(self.label("Amount"), (add_mm(payment_left, mm(12)), currency_top), **payment_head_font))
-        grp.add(dwg.text(self.currency, (payment_left, mm(77)), **self.font_info))
+        grp.add(dwg.text(self.currency, (payment_left, mm(currency_top + 5)), **self.font_info))
         if self.amount:
             grp.add(dwg.text(
                 format_amount(self.amount),
@@ -646,7 +658,7 @@ class QRBill:
             )
 
         # Right side of the bill
-        y_pos = 10
+        y_pos = 10 + above_padding
         line_space = 3.5
 
         def add_header(text, first=False):


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 579a28d..37d0ba7 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -23,7 +23,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: ["3.6", "3.7", "3.8", "3.9"]
+        python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
     steps:
       - uses: actions/checkout@v2
       - name: Set up Python ${{ matrix.python-version }}
diff --git a/tests/test_qrbill.py b/tests/test_qrbill.py
index 02339da..8c5f220 100644
--- a/tests/test_qrbill.py
+++ b/tests/test_qrbill.py
@@ -329,21 +329,21 @@ class QRBillTests(unittest.TestCase):
             '<text {font9} x="{x}" y="{y5}">Payable by </text>'
             '<text {font10} x="{x}" y="{y6}">31.10.2019</text>'.format(
                 font9=font9, font10=font10, x='418.11023',
-                y1=mm(57.5), y2=mm(61), y3=mm(64.5), y4=mm(68), y5=mm(74.5), y6=mm(78),
+                y1=mm(58.5), y2=mm(62), y3=mm(65.5), y4=mm(69), y5=mm(75.5), y6=mm(79),
             )
         )
         self.assertIn(expected, content)
         # IBAN formatted
         self.assertIn(
             '<text {font10} x="{x}" y="{y}">CH44 3199 9123 0008 8901 2</text>'.format(
-                font10=font10, x=mm(5), y=mm(18.5),
+                font10=font10, x=mm(5), y=mm(19.5),
             ),
             content
         )
         # amount formatted
         self.assertIn(
             '<text {font10} x="{x}" y="{y}">1 949.70</text>'.format(
-                font10=font10, x=mm(17), y=mm(77),
+                font10=font10, x=mm(17), y=mm(78),
             ),
             content
         )
@@ -437,14 +437,17 @@ class QRBillTests(unittest.TestCase):
             },
             font_factor=1.5
         )
+        content = strip_svg_path(self._produce_svg(bill))
         self.assertIn(
             '<text font-family="Helvetica" font-size="18.0" font-weight="bold"'
-            ' x="17.71654" y="35.43307">Receipt</text>'
+            ' x="17.71654" y="{y1}">Receipt</text>'
             '<text font-family="Helvetica" font-size="12.0" font-weight="bold"'
-            ' x="17.71654" y="53.14961">Account / Payable to</text>'
+            ' x="17.71654" y="{y2}">Account / Payable to</text>'
             '<text font-family="Helvetica" font-size="15.0" x="17.71654"'
-            ' y="65.55118">CH53 8000 5000 0102 8366 4</text>',
-            self._produce_svg(bill)
+            ' y="{y3}">CH53 8000 5000 0102 8366 4</text>'.format(
+                y1=mm(11), y2=mm(16), y3=mm(19.5)
+            ),
+            content
         )