qrcode
というパッケージを使う。
8月半ばに作ったWebアプリでXMLに変換したQRコードを使っている。
今回はさらに、SVGをXMLにタグに変換してHTMLに埋め込むところまでをやっていく。
コードは以下のGistで公開している。
PythonでQRコードを生成してSVGタグに変換する · GitHub
全体の流れ
def main(): # QRコードに埋め込むテキストの読み込み with open('zen.txt', encoding='utf-8') as f: text = f.read() # QRコードのSVG作成 qr = make_qrcode_svg(text) # DOMオブジェクトを作成 dom = create_dom(qr) # DOMをXMLに整形 xml = dom.toprettyxml() HTML = f''' <!DOCTYPE html> <head> <title>sample QR</title> </head> <body> {xml} </body> </html> ''' # HTMLに埋め込み with open('sample.html', 'w', encoding='utf-8') as f: f.write(HTML)
QRコードのSVGを作る
import xml.dom.minidom import qrcode import qrcode.image.svg as svg def make_qrcode_svg(text): """ QRコードのSVGを作る関数 :param text: QRコードに埋め込みたい文字列 :return: QRコードのSVG """ factory = svg.SvgPathImage img = qrcode.make( text, image_factory=factory, version=1, box_size=6, error_correction=qrcode.constants.ERROR_CORRECT_L) return img
XMLの構造
具体的にXMLにする方法を説明する前に、XMLの構造を簡単にまとめる。
以下が今回、生成するXML。
<?xml version="1.0" ?> <svg height="21mm" viewBox="0 0 21 21" width="21mm" xmlns="http://www.w3.org/2000/svg"> <path d=". . ."> </svg>
初めにXML宣言がある。<?xml version="1.0" ?>
の部分。バージョンや文字コードを定義する。
XMLにおいてタグは要素と呼ぶ(要素ノードとも呼ばれる)。
要素には属性を設定できる(属性もまた、属性ノードとも呼ばれる)。
svg
要素のheight
属性、というような感じ。
また要素には子要素を設定できる。path
要素がsvg
要素の子要素にあたる。
XMLを生成する
標準ライブラリのxml.dom.minidom
を使う。以下のQiita記事を参考にした。
def create_dom(img): """ QRコードのsvgをXMLとして出力する関数 :param img: QRコードのsvg :return dom: xml.dom.minidom.Document Object """ width = img.width items = img.get_image() path = img.make_path() # DOM dom = xml.dom.minidom.Document() # svg要素を作成 elem = dom.createElement('svg') # DOMに子要素elemを追加 dom.appendChild(elem) # svgからxmlns属性を取得 xmlns = items.get('xmlns') # svg要素の属性を指定 values = { 'width': f'{width}mm', 'height': f'{width}mm', 'viewBox': f'0 0 {width} {width}', 'xmlns': xmlns } # DOMにsvg要素を設定 set_dom_attr(dom, elem, values) # path要素を作成 path_node = dom.createElement('path') # elem要素に子要素(path_node)を追加 elem.appendChild(path_node) # pathからd属性を取得 d = path.get('d') # pathからid属性を取得 id_ = path.get('id') # pathからstyle属性を取得 style = path.get('style') # path要素の属性を指定 values = { 'd': d, 'id': id_, 'style': style, } # DOMにpath要素を設定 set_dom_attr(dom, path_node, values) return dom
def set_dom_attr(dom, elem, values): """ 属性ノードを生成して要素を設定する関数 :param dom: xml.dom.minidom.Document Object :param elem: 要素 :param values: 属性 :return: """ for attr, value in values.items(): # 属性ノードを生成 subnode_attrs = dom.createAttribute(attr) # 属性ノードの値を設定 subnode_attrs.value = value # 属性ノードを要素ノードにセットする elem.setAttributeNode(subnode_attrs)
以下のようにHTMLが出力される(path
要素の中身は長すぎるので省略。
<!DOCTYPE html> <head> <title>sample QR</title> </head> <body> <?xml version="1.0" ?> <svg height="21mm" viewBox="0 0 21 21" width="21mm" xmlns="http://www.w3.org/2000/svg"> <path d=". . ."> </svg> </body> </html>
出力されたHTMLは以下のようになる。
Webアプリで使う場合はこのQRコードの部分(今回はxml
変数)にテンプレートエンジンのフィルター(Jinja2
ではsafe
フィルター)を掛ければHTML埋め込むことができる。