使用 Python3 创建带有 SVG 边框模板的二维码

Posted

技术标签:

【中文标题】使用 Python3 创建带有 SVG 边框模板的二维码【英文标题】:Create QR codes with a SVG border template using Python3 【发布时间】:2019-11-15 21:49:51 【问题描述】:

我正在使用 Python 二维码库生成 SVG 二维码:

factory = qrcode.image.svg.SvgImage
img = qr.make_image(fill_color="black", back_color="white", image_factory=factory)

我有一个单独的 SVG 图像 (border.svg)。如何将 QR 码对象 (img) 集成到边框 svg 中以生成一个组合 svg 文件?我发现很多 svg 库可以转换为 png/eps,但没有一个能够简单地整合两个图像。

奖励:必须缩放边框(或 QR 图像)以适合,因为根据 QR 码的数据大小,QR svg 的尺寸会有所不同。

【问题讨论】:

border.svg 是什么样的,是二维码周围需要复制的图案,还是二维码空白处的边框正方形? 它是一个带有空白点的正方形。 【参考方案1】:

我为 python SVG 模块做了一些探索,但我没有想出任何让问题变得更简单的方法。

SVG 实际上只是 XML。因此,您可以编辑 SVG 的 XML 并更改 SVG。 Python 确实有很好的内置对 XML 的支持(实际上是 *ML)。

基本思想是获取边界并获取其大小。然后,我们使用大小缩放 qr 代码以适合边界内。然后我们将 qr 添加到边界 SVG-XML 并保存到新文件中。

from lxml import etree

# Create XML objects
boarder = etree.parse('boarder.svg')
qr = etree.parse('qr.svg') # ET.parse('qr.svg').getroot()

# Get size of boarder
Bhight = int(boarder.xpath('//*[local-name()="svg"]/@width')[0])
Bwidth = int(boarder.xpath('//*[local-name()="svg"]/@height')[0])
# Make sure that it is a square
assert(Bhight == Bwidth)

# resize qr code for boarder.
qrBack = qr.xpath('//*[local-name()="svg"]//*[local-name()="g"]//*[local-name()="rect"]')[0]

# Also needs to be a square
assert(qrBack.attrib['height'] == qrBack.attrib['width'])

# Calc offset code from boarder
qrWidth = int(qrBack.attrib['width'])
offset = (Bwidth - qrWidth) / 2

# Add offset attribute
qr.xpath('//*[local-name()="svg"]//*[local-name()="g"]')[0].attrib['transform'] = 'translate(0,0)'.format(offset)

# get qr code
QRC = qr.xpath('//*[local-name()="svg"]//*[local-name()="g"]')[0]
# Take Boarder as xml root
root = boarder.getroot()
# Add QRC to root
root.append(QRC)
# Write new svg to file
with open( 'boarder+qr.svg', 'w' ) as f:
    f.write( etree.tostring( root, pretty_print=True, xml_declaration=True, encoding='UTF-8', standalone="yes").decode() )

创建文件boarder+qr.svg

供参考:

寄宿生.svg

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   id="svg8"
   version="1.1"
   
   >
  <defs
     id="defs2" />
  <metadata
     id="metadata5">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title></dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     id="layer1">
    <path
       id="rect1379"
       d="m 0,0 h 110 v 110 H 0 Z 0"
       style="
          stroke:#FFFFFF;
          stroke-width:0.89999998;
          stroke-linecap:round;
          stroke-linejoin:round;
          stroke-miterlimit:4;
          stroke-dasharray:none;
          stroke-dashoffset:0;
          stroke-opacity:1"
          />
  </g>
</svg>

qr.svg

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
   "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg   version="1.1"
   xmlns="http://www.w3.org/2000/svg">
   <desc>Zint Generated Symbol
   </desc>

   <g id="barcode" fill="#000000">
      <rect x="0" y="0"   fill="#FFFFFF" />
      <rect x="12.00" y="12.00"   />
      <rect x="36.00" y="12.00"   />
      <rect x="54.00" y="12.00"   />
      <rect x="60.00" y="12.00"   />
      <rect x="66.00" y="12.00"   />
      <rect x="12.00" y="15.00"   />
      <rect x="30.00" y="15.00"   />
      <rect x="36.00" y="15.00"   />
      <rect x="45.00" y="15.00"   />
      <rect x="51.00" y="15.00"   />
      <rect x="60.00" y="15.00"   />
      <rect x="66.00" y="15.00"   />
      <rect x="84.00" y="15.00"   />
      <rect x="12.00" y="18.00"   />
      <rect x="18.00" y="18.00"   />
      <rect x="30.00" y="18.00"   />
      <rect x="39.00" y="18.00"   />
      <rect x="51.00" y="18.00"   />
      <rect x="57.00" y="18.00"   />
      <rect x="66.00" y="18.00"   />
      <rect x="72.00" y="18.00"   />
      <rect x="84.00" y="18.00"   />
      <rect x="12.00" y="21.00"   />
      <rect x="18.00" y="21.00"   />
      <rect x="30.00" y="21.00"   />
      <rect x="45.00" y="21.00"   />
      <rect x="57.00" y="21.00"   />
      <rect x="66.00" y="21.00"   />
      <rect x="72.00" y="21.00"   />
      <rect x="84.00" y="21.00"   />
      <rect x="12.00" y="24.00"   />
      <rect x="18.00" y="24.00"   />
      <rect x="30.00" y="24.00"   />
      <rect x="36.00" y="24.00"   />
      <rect x="45.00" y="24.00"   />
      <rect x="66.00" y="24.00"   />
      <rect x="72.00" y="24.00"   />
      <rect x="84.00" y="24.00"   />
      <rect x="12.00" y="27.00"   />
      <rect x="30.00" y="27.00"   />
      <rect x="36.00" y="27.00"   />
      <rect x="48.00" y="27.00"   />
      <rect x="60.00" y="27.00"   />
      <rect x="66.00" y="27.00"   />
      <rect x="84.00" y="27.00"   />
      <rect x="12.00" y="30.00"   />
      <rect x="36.00" y="30.00"   />
      <rect x="42.00" y="30.00"   />
      <rect x="48.00" y="30.00"   />
      <rect x="54.00" y="30.00"   />
      <rect x="60.00" y="30.00"   />
      <rect x="66.00" y="30.00"   />
      <rect x="36.00" y="33.00"   />
      <rect x="57.00" y="33.00"   />
      <rect x="12.00" y="36.00"   />
      <rect x="27.00" y="36.00"   />
      <rect x="36.00" y="36.00"   />
      <rect x="45.00" y="36.00"   />
      <rect x="60.00" y="36.00"   />
      <rect x="81.00" y="36.00"   />
      <rect x="12.00" y="39.00"   />
      <rect x="18.00" y="39.00"   />
      <rect x="24.00" y="39.00"   />
      <rect x="33.00" y="39.00"   />
      <rect x="48.00" y="39.00"   />
      <rect x="54.00" y="39.00"   />
      <rect x="66.00" y="39.00"   />
      <rect x="75.00" y="39.00"   />
      <rect x="81.00" y="39.00"   />
      <rect x="15.00" y="42.00"   />
      <rect x="21.00" y="42.00"   />
      <rect x="30.00" y="42.00"   />
      <rect x="36.00" y="42.00"   />
      <rect x="48.00" y="42.00"   />
      <rect x="57.00" y="42.00"   />
      <rect x="72.00" y="42.00"   />
      <rect x="84.00" y="42.00"   />
      <rect x="18.00" y="45.00"   />
      <rect x="33.00" y="45.00"   />
      <rect x="42.00" y="45.00"   />
      <rect x="57.00" y="45.00"   />
      <rect x="69.00" y="45.00"   />
      <rect x="75.00" y="45.00"   />
      <rect x="15.00" y="48.00"   />
      <rect x="24.00" y="48.00"   />
      <rect x="39.00" y="48.00"   />
      <rect x="48.00" y="48.00"   />
      <rect x="57.00" y="48.00"   />
      <rect x="66.00" y="48.00"   />
      <rect x="84.00" y="48.00"   />
      <rect x="21.00" y="51.00"   />
      <rect x="39.00" y="51.00"   />
      <rect x="57.00" y="51.00"   />
      <rect x="66.00" y="51.00"   />
      <rect x="81.00" y="51.00"   />
      <rect x="12.00" y="54.00"   />
      <rect x="30.00" y="54.00"   />
      <rect x="39.00" y="54.00"   />
      <rect x="69.00" y="54.00"   />
      <rect x="84.00" y="54.00"   />
      <rect x="18.00" y="57.00"   />
      <rect x="24.00" y="57.00"   />
      <rect x="33.00" y="57.00"   />
      <rect x="39.00" y="57.00"   />
      <rect x="45.00" y="57.00"   />
      <rect x="54.00" y="57.00"   />
      <rect x="69.00" y="57.00"   />
      <rect x="12.00" y="60.00"   />
      <rect x="21.00" y="60.00"   />
      <rect x="30.00" y="60.00"   />
      <rect x="39.00" y="60.00"   />
      <rect x="60.00" y="60.00"   />
      <rect x="81.00" y="60.00"   />
      <rect x="36.00" y="63.00"   />
      <rect x="60.00" y="63.00"   />
      <rect x="72.00" y="63.00"   />
      <rect x="78.00" y="63.00"   />
      <rect x="84.00" y="63.00"   />
      <rect x="12.00" y="66.00"   />
      <rect x="45.00" y="66.00"   />
      <rect x="54.00" y="66.00"   />
      <rect x="60.00" y="66.00"   />
      <rect x="66.00" y="66.00"   />
      <rect x="72.00" y="66.00"   />
      <rect x="84.00" y="66.00"   />
      <rect x="12.00" y="69.00"   />
      <rect x="30.00" y="69.00"   />
      <rect x="36.00" y="69.00"   />
      <rect x="54.00" y="69.00"   />
      <rect x="60.00" y="69.00"   />
      <rect x="72.00" y="69.00"   />
      <rect x="84.00" y="69.00"   />
      <rect x="12.00" y="72.00"   />
      <rect x="18.00" y="72.00"   />
      <rect x="30.00" y="72.00"   />
      <rect x="45.00" y="72.00"   />
      <rect x="54.00" y="72.00"   />
      <rect x="81.00" y="72.00"   />
      <rect x="12.00" y="75.00"   />
      <rect x="18.00" y="75.00"   />
      <rect x="30.00" y="75.00"   />
      <rect x="42.00" y="75.00"   />
      <rect x="54.00" y="75.00"   />
      <rect x="60.00" y="75.00"   />
      <rect x="72.00" y="75.00"   />
      <rect x="12.00" y="78.00"   />
      <rect x="18.00" y="78.00"   />
      <rect x="30.00" y="78.00"   />
      <rect x="36.00" y="78.00"   />
      <rect x="51.00" y="78.00"   />
      <rect x="57.00" y="78.00"   />
      <rect x="72.00" y="78.00"   />
      <rect x="81.00" y="78.00"   />
      <rect x="12.00" y="81.00"   />
      <rect x="30.00" y="81.00"   />
      <rect x="36.00" y="81.00"   />
      <rect x="54.00" y="81.00"   />
      <rect x="69.00" y="81.00"   />
      <rect x="12.00" y="84.00"   />
      <rect x="36.00" y="84.00"   />
      <rect x="51.00" y="84.00"   />
      <rect x="60.00" y="84.00"   />
      <rect x="75.00" y="84.00"   />
      <rect x="84.00" y="84.00"   />
   </g>
</svg>

您可能需要更改一些内容,以便正确定位源 SVG,但这应该会给您一个很好的起点。

寄宿生+qr.svg

<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" id="svg8" version="1.1"  >
  <defs id="defs2"/>
  <metadata id="metadata5">
    <rdf:RDF>
      <cc:Work rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
        <dc:title/>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g id="layer1">
    <path id="rect1379" d="m 0,0 h 110 v 110 H 0 Z 0" style="        stroke:#FFFFFF;        stroke-width:0.89999998;        stroke-linecap:round;        stroke-linejoin:round;        stroke-miterlimit:4;        stroke-dasharray:none;        stroke-dashoffset:0;        stroke-opacity:1"/>
  </g>
<svg:g id="barcode" fill="#000000" transform="translate(5.0,5.0)">
      <svg:rect x="0" y="0"   fill="#FFFFFF"/>
      <svg:rect x="12.00" y="12.00"  />
      <svg:rect x="36.00" y="12.00"  />
      <svg:rect x="54.00" y="12.00"  />
      <svg:rect x="60.00" y="12.00"  />
      <svg:rect x="66.00" y="12.00"  />
      <svg:rect x="12.00" y="15.00"  />
      <svg:rect x="30.00" y="15.00"  />
      <svg:rect x="36.00" y="15.00"  />
      <svg:rect x="45.00" y="15.00"  />
      <svg:rect x="51.00" y="15.00"  />
      <svg:rect x="60.00" y="15.00"  />
      <svg:rect x="66.00" y="15.00"  />
      <svg:rect x="84.00" y="15.00"  />
      <svg:rect x="12.00" y="18.00"  />
      <svg:rect x="18.00" y="18.00"  />
      <svg:rect x="30.00" y="18.00"  />
      <svg:rect x="39.00" y="18.00"  />
      <svg:rect x="51.00" y="18.00"  />
      <svg:rect x="57.00" y="18.00"  />
      <svg:rect x="66.00" y="18.00"  />
      <svg:rect x="72.00" y="18.00"  />
      <svg:rect x="84.00" y="18.00"  />
      <svg:rect x="12.00" y="21.00"  />
      <svg:rect x="18.00" y="21.00"  />
      <svg:rect x="30.00" y="21.00"  />
      <svg:rect x="45.00" y="21.00"  />
      <svg:rect x="57.00" y="21.00"  />
      <svg:rect x="66.00" y="21.00"  />
      <svg:rect x="72.00" y="21.00"  />
      <svg:rect x="84.00" y="21.00"  />
      <svg:rect x="12.00" y="24.00"  />
      <svg:rect x="18.00" y="24.00"  />
      <svg:rect x="30.00" y="24.00"  />
      <svg:rect x="36.00" y="24.00"  />
      <svg:rect x="45.00" y="24.00"  />
      <svg:rect x="66.00" y="24.00"  />
      <svg:rect x="72.00" y="24.00"  />
      <svg:rect x="84.00" y="24.00"  />
      <svg:rect x="12.00" y="27.00"  />
      <svg:rect x="30.00" y="27.00"  />
      <svg:rect x="36.00" y="27.00"  />
      <svg:rect x="48.00" y="27.00"  />
      <svg:rect x="60.00" y="27.00"  />
      <svg:rect x="66.00" y="27.00"  />
      <svg:rect x="84.00" y="27.00"  />
      <svg:rect x="12.00" y="30.00"  />
      <svg:rect x="36.00" y="30.00"  />
      <svg:rect x="42.00" y="30.00"  />
      <svg:rect x="48.00" y="30.00"  />
      <svg:rect x="54.00" y="30.00"  />
      <svg:rect x="60.00" y="30.00"  />
      <svg:rect x="66.00" y="30.00"  />
      <svg:rect x="36.00" y="33.00"  />
      <svg:rect x="57.00" y="33.00"  />
      <svg:rect x="12.00" y="36.00"  />
      <svg:rect x="27.00" y="36.00"  />
      <svg:rect x="36.00" y="36.00"  />
      <svg:rect x="45.00" y="36.00"  />
      <svg:rect x="60.00" y="36.00"  />
      <svg:rect x="81.00" y="36.00"  />
      <svg:rect x="12.00" y="39.00"  />
      <svg:rect x="18.00" y="39.00"  />
      <svg:rect x="24.00" y="39.00"  />
      <svg:rect x="33.00" y="39.00"  />
      <svg:rect x="48.00" y="39.00"  />
      <svg:rect x="54.00" y="39.00"  />
      <svg:rect x="66.00" y="39.00"  />
      <svg:rect x="75.00" y="39.00"  />
      <svg:rect x="81.00" y="39.00"  />
      <svg:rect x="15.00" y="42.00"  />
      <svg:rect x="21.00" y="42.00"  />
      <svg:rect x="30.00" y="42.00"  />
      <svg:rect x="36.00" y="42.00"  />
      <svg:rect x="48.00" y="42.00"  />
      <svg:rect x="57.00" y="42.00"  />
      <svg:rect x="72.00" y="42.00"  />
      <svg:rect x="84.00" y="42.00"  />
      <svg:rect x="18.00" y="45.00"  />
      <svg:rect x="33.00" y="45.00"  />
      <svg:rect x="42.00" y="45.00"  />
      <svg:rect x="57.00" y="45.00"  />
      <svg:rect x="69.00" y="45.00"  />
      <svg:rect x="75.00" y="45.00"  />
      <svg:rect x="15.00" y="48.00"  />
      <svg:rect x="24.00" y="48.00"  />
      <svg:rect x="39.00" y="48.00"  />
      <svg:rect x="48.00" y="48.00"  />
      <svg:rect x="57.00" y="48.00"  />
      <svg:rect x="66.00" y="48.00"  />
      <svg:rect x="84.00" y="48.00"  />
      <svg:rect x="21.00" y="51.00"  />
      <svg:rect x="39.00" y="51.00"  />
      <svg:rect x="57.00" y="51.00"  />
      <svg:rect x="66.00" y="51.00"  />
      <svg:rect x="81.00" y="51.00"  />
      <svg:rect x="12.00" y="54.00"  />
      <svg:rect x="30.00" y="54.00"  />
      <svg:rect x="39.00" y="54.00"  />
      <svg:rect x="69.00" y="54.00"  />
      <svg:rect x="84.00" y="54.00"  />
      <svg:rect x="18.00" y="57.00"  />
      <svg:rect x="24.00" y="57.00"  />
      <svg:rect x="33.00" y="57.00"  />
      <svg:rect x="39.00" y="57.00"  />
      <svg:rect x="45.00" y="57.00"  />
      <svg:rect x="54.00" y="57.00"  />
      <svg:rect x="69.00" y="57.00"  />
      <svg:rect x="12.00" y="60.00"  />
      <svg:rect x="21.00" y="60.00"  />
      <svg:rect x="30.00" y="60.00"  />
      <svg:rect x="39.00" y="60.00"  />
      <svg:rect x="60.00" y="60.00"  />
      <svg:rect x="81.00" y="60.00"  />
      <svg:rect x="36.00" y="63.00"  />
      <svg:rect x="60.00" y="63.00"  />
      <svg:rect x="72.00" y="63.00"  />
      <svg:rect x="78.00" y="63.00"  />
      <svg:rect x="84.00" y="63.00"  />
      <svg:rect x="12.00" y="66.00"  />
      <svg:rect x="45.00" y="66.00"  />
      <svg:rect x="54.00" y="66.00"  />
      <svg:rect x="60.00" y="66.00"  />
      <svg:rect x="66.00" y="66.00"  />
      <svg:rect x="72.00" y="66.00"  />
      <svg:rect x="84.00" y="66.00"  />
      <svg:rect x="12.00" y="69.00"  />
      <svg:rect x="30.00" y="69.00"  />
      <svg:rect x="36.00" y="69.00"  />
      <svg:rect x="54.00" y="69.00"  />
      <svg:rect x="60.00" y="69.00"  />
      <svg:rect x="72.00" y="69.00"  />
      <svg:rect x="84.00" y="69.00"  />
      <svg:rect x="12.00" y="72.00"  />
      <svg:rect x="18.00" y="72.00"  />
      <svg:rect x="30.00" y="72.00"  />
      <svg:rect x="45.00" y="72.00"  />
      <svg:rect x="54.00" y="72.00"  />
      <svg:rect x="81.00" y="72.00"  />
      <svg:rect x="12.00" y="75.00"  />
      <svg:rect x="18.00" y="75.00"  />
      <svg:rect x="30.00" y="75.00"  />
      <svg:rect x="42.00" y="75.00"  />
      <svg:rect x="54.00" y="75.00"  />
      <svg:rect x="60.00" y="75.00"  />
      <svg:rect x="72.00" y="75.00"  />
      <svg:rect x="12.00" y="78.00"  />
      <svg:rect x="18.00" y="78.00"  />
      <svg:rect x="30.00" y="78.00"  />
      <svg:rect x="36.00" y="78.00"  />
      <svg:rect x="51.00" y="78.00"  />
      <svg:rect x="57.00" y="78.00"  />
      <svg:rect x="72.00" y="78.00"  />
      <svg:rect x="81.00" y="78.00"  />
      <svg:rect x="12.00" y="81.00"  />
      <svg:rect x="30.00" y="81.00"  />
      <svg:rect x="36.00" y="81.00"  />
      <svg:rect x="54.00" y="81.00"  />
      <svg:rect x="69.00" y="81.00"  />
      <svg:rect x="12.00" y="84.00"  />
      <svg:rect x="36.00" y="84.00"  />
      <svg:rect x="51.00" y="84.00"  />
      <svg:rect x="60.00" y="84.00"  />
      <svg:rect x="75.00" y="84.00"  />
      <svg:rect x="84.00" y="84.00"  />
   </svg:g>
</svg>

【讨论】:

以上是关于使用 Python3 创建带有 SVG 边框模板的二维码的主要内容,如果未能解决你的问题,请参考以下文章

带有vue和svg的弯曲底部边框[重复]

CSS和创建svg图形的边框

带有svg图像模板的angularjs

是否可以使用 SVG 矩形作为具有可变高度的 HTML 元素的边框

在 SVG 中绘制文本但删除背景

在 SVG 中绘制文本但删除背景