diff --git a/PKG-INFO b/PKG-INFO new file mode 100644 index 0000000..c88669b --- /dev/null +++ b/PKG-INFO @@ -0,0 +1,93 @@ +Metadata-Version: 1.1 +Name: PyQRCode +Version: 1.2.1 +Summary: A QR code generator written purely in Python with SVG, EPS, PNG and terminal output. +Home-page: https://github.com/mnooner256/pyqrcode +Author: Michael Nooner +Author-email: mnooner256@gmail.com +License: BSD +Description: ======== + PyQRCode + ======== + + .. contents:: + + The pyqrcode module is a QR code generator that is simple to use and written + in pure python. The module can automates most of the building process for + creating QR codes. Most codes can be created using only two lines of code! + + Unlike other generators, all of the helpers can be controlled manually. You are + free to set any or all of the properties of your QR code. + + QR codes can be saved as SVG, PNG (by using the + `pypng `_ module), and plain text. They can + also be displayed directly in most Linux terminal emulators. PIL is + not used to render the image files. + + The pyqrcode module attempts to follow the QR code standard as closely as + possible. The terminology and the encodings used in pyqrcode come directly + from the standard. This module also follows the algorithm laid out in the + standard. + + **Homepage**: https://github.com/mnooner256/pyqrcode + + **Documentation**: http://pythonhosted.org/PyQRCode/ + + Requirements + ============ + + The pyqrcode module only requires Python 2.6, Python 2.7, or Python 3. You may + want to install `pypng `_ in order to + render PNG files, but it is optional. Note, pypng is a pure python PNG writer + which does not require any other libraries. + + Installation + ============ + + Installation is simple. It can be installed from pip using the following + command:: + + $ pip install pyqrcode + + Or from the terminal:: + + $ python setup.py install + + + Usage + ===== + + The pyqrcode module aims to be as simple to use as possible. Below is a simple + example of creating a QR code for a URL. The code is rendered out as an svg + file. + :: + + >>> import pyqrcode + >>> url = pyqrcode.create('http://uca.edu') + >>> url.svg('uca-url.svg', scale=8) + >>> url.eps('uca-url.eps', scale=2) + >>> print(url.terminal(quiet_zone=1)) + + The pyqrcode module, while easy to use, is powerful. You can set every + property of the QR code. If you install the optional + `pypng `_ module, you can + render the code as a PNG image. Below is a more complex example:: + + >>> big_code = pyqrcode.create('0987654321', error='L', version=27, mode='binary') + >>> big_code.png('code.png', scale=6, module_color=[0, 0, 0, 128], background=[0xff, 0xff, 0xcc]) + >>> big_code.show() + +Keywords: qrcode,qr +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Operating System :: POSIX +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 diff --git a/PyQRCode.egg-info/PKG-INFO b/PyQRCode.egg-info/PKG-INFO new file mode 100644 index 0000000..c88669b --- /dev/null +++ b/PyQRCode.egg-info/PKG-INFO @@ -0,0 +1,93 @@ +Metadata-Version: 1.1 +Name: PyQRCode +Version: 1.2.1 +Summary: A QR code generator written purely in Python with SVG, EPS, PNG and terminal output. +Home-page: https://github.com/mnooner256/pyqrcode +Author: Michael Nooner +Author-email: mnooner256@gmail.com +License: BSD +Description: ======== + PyQRCode + ======== + + .. contents:: + + The pyqrcode module is a QR code generator that is simple to use and written + in pure python. The module can automates most of the building process for + creating QR codes. Most codes can be created using only two lines of code! + + Unlike other generators, all of the helpers can be controlled manually. You are + free to set any or all of the properties of your QR code. + + QR codes can be saved as SVG, PNG (by using the + `pypng `_ module), and plain text. They can + also be displayed directly in most Linux terminal emulators. PIL is + not used to render the image files. + + The pyqrcode module attempts to follow the QR code standard as closely as + possible. The terminology and the encodings used in pyqrcode come directly + from the standard. This module also follows the algorithm laid out in the + standard. + + **Homepage**: https://github.com/mnooner256/pyqrcode + + **Documentation**: http://pythonhosted.org/PyQRCode/ + + Requirements + ============ + + The pyqrcode module only requires Python 2.6, Python 2.7, or Python 3. You may + want to install `pypng `_ in order to + render PNG files, but it is optional. Note, pypng is a pure python PNG writer + which does not require any other libraries. + + Installation + ============ + + Installation is simple. It can be installed from pip using the following + command:: + + $ pip install pyqrcode + + Or from the terminal:: + + $ python setup.py install + + + Usage + ===== + + The pyqrcode module aims to be as simple to use as possible. Below is a simple + example of creating a QR code for a URL. The code is rendered out as an svg + file. + :: + + >>> import pyqrcode + >>> url = pyqrcode.create('http://uca.edu') + >>> url.svg('uca-url.svg', scale=8) + >>> url.eps('uca-url.eps', scale=2) + >>> print(url.terminal(quiet_zone=1)) + + The pyqrcode module, while easy to use, is powerful. You can set every + property of the QR code. If you install the optional + `pypng `_ module, you can + render the code as a PNG image. Below is a more complex example:: + + >>> big_code = pyqrcode.create('0987654321', error='L', version=27, mode='binary') + >>> big_code.png('code.png', scale=6, module_color=[0, 0, 0, 128], background=[0xff, 0xff, 0xcc]) + >>> big_code.show() + +Keywords: qrcode,qr +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Operating System :: POSIX +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 diff --git a/PyQRCode.egg-info/SOURCES.txt b/PyQRCode.egg-info/SOURCES.txt new file mode 100644 index 0000000..e1bcc62 --- /dev/null +++ b/PyQRCode.egg-info/SOURCES.txt @@ -0,0 +1,10 @@ +README.rst +setup.py +PyQRCode.egg-info/PKG-INFO +PyQRCode.egg-info/SOURCES.txt +PyQRCode.egg-info/dependency_links.txt +PyQRCode.egg-info/requires.txt +PyQRCode.egg-info/top_level.txt +pyqrcode/__init__.py +pyqrcode/builder.py +pyqrcode/tables.py \ No newline at end of file diff --git a/PyQRCode.egg-info/dependency_links.txt b/PyQRCode.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/PyQRCode.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/PyQRCode.egg-info/requires.txt b/PyQRCode.egg-info/requires.txt new file mode 100644 index 0000000..1612941 --- /dev/null +++ b/PyQRCode.egg-info/requires.txt @@ -0,0 +1,4 @@ + + +[PNG] +pypng>=0.0.13 \ No newline at end of file diff --git a/PyQRCode.egg-info/top_level.txt b/PyQRCode.egg-info/top_level.txt new file mode 100644 index 0000000..6f17668 --- /dev/null +++ b/PyQRCode.egg-info/top_level.txt @@ -0,0 +1 @@ +pyqrcode diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..7cf0cf4 --- /dev/null +++ b/README.rst @@ -0,0 +1,70 @@ +======== +PyQRCode +======== + +.. contents:: + +The pyqrcode module is a QR code generator that is simple to use and written +in pure python. The module can automates most of the building process for +creating QR codes. Most codes can be created using only two lines of code! + +Unlike other generators, all of the helpers can be controlled manually. You are +free to set any or all of the properties of your QR code. + +QR codes can be saved as SVG, PNG (by using the +`pypng `_ module), and plain text. They can +also be displayed directly in most Linux terminal emulators. PIL is +not used to render the image files. + +The pyqrcode module attempts to follow the QR code standard as closely as +possible. The terminology and the encodings used in pyqrcode come directly +from the standard. This module also follows the algorithm laid out in the +standard. + +**Homepage**: https://github.com/mnooner256/pyqrcode + +**Documentation**: http://pythonhosted.org/PyQRCode/ + +Requirements +============ + +The pyqrcode module only requires Python 2.6, Python 2.7, or Python 3. You may +want to install `pypng `_ in order to +render PNG files, but it is optional. Note, pypng is a pure python PNG writer +which does not require any other libraries. + +Installation +============ + +Installation is simple. It can be installed from pip using the following +command:: + + $ pip install pyqrcode + +Or from the terminal:: + + $ python setup.py install + + +Usage +===== + +The pyqrcode module aims to be as simple to use as possible. Below is a simple +example of creating a QR code for a URL. The code is rendered out as an svg +file. +:: + + >>> import pyqrcode + >>> url = pyqrcode.create('http://uca.edu') + >>> url.svg('uca-url.svg', scale=8) + >>> url.eps('uca-url.eps', scale=2) + >>> print(url.terminal(quiet_zone=1)) + +The pyqrcode module, while easy to use, is powerful. You can set every +property of the QR code. If you install the optional +`pypng `_ module, you can +render the code as a PNG image. Below is a more complex example:: + + >>> big_code = pyqrcode.create('0987654321', error='L', version=27, mode='binary') + >>> big_code.png('code.png', scale=6, module_color=[0, 0, 0, 128], background=[0xff, 0xff, 0xcc]) + >>> big_code.show() diff --git a/pyqrcode/__init__.py b/pyqrcode/__init__.py new file mode 100644 index 0000000..8301f8b --- /dev/null +++ b/pyqrcode/__init__.py @@ -0,0 +1,691 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2013, Michael Nooner +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +"""This module is used to create QR Codes. It is designed to be as simple and +as possible. It does this by using sane defaults and autodetection to make +creating a QR Code very simple. + +It is recommended that you use the :func:`pyqrcode.create` function to build the +QRCode object. This results in cleaner looking code. + +Examples: + >>> import pyqrcode + >>> import sys + >>> url = pyqrcode.create('http://uca.edu') + >>> url.svg(sys.stdout, scale=1) + >>> url.svg('uca.svg', scale=4) + >>> number = pyqrcode.create(123456789012345) + >>> number.png('big-number.png') +""" + +#Imports required for 2.7 support +from __future__ import absolute_import, division, print_function, with_statement, unicode_literals + +import pyqrcode.tables +import pyqrcode.builder as builder + +try: + str = unicode # Python 2 +except NameError: + pass + +def create(content, error='H', version=None, mode=None, encoding=None): + """When creating a QR code only the content to be encoded is required, + all the other properties of the code will be guessed based on the + contents given. This function will return a :class:`QRCode` object. + + Unless you are familiar with QR code's inner workings + it is recommended that you just specify the *content* and nothing else. + However, there are cases where you may want to specify the various + properties of the created code manually, this is what the other + parameters do. Below, you will find a lengthy explanation of what + each parameter is for. Note, the parameter names and values are taken + directly from the standards. You may need to familiarize yourself + with the terminology of QR codes for the names and their values to + make sense. + + The *error* parameter sets the error correction level of the code. There + are four levels defined by the standard. The first is level 'L' which + allows for 7% of the code to be corrected. Second, is level 'M' which + allows for 15% of the code to be corrected. Next, is level 'Q' which + is the most common choice for error correction, it allow 25% of the + code to be corrected. Finally, there is the highest level 'H' which + allows for 30% of the code to be corrected. There are several ways to + specify this parameter, you can use an upper or lower case letter, + a float corresponding to the percentage of correction, or a string + containing the percentage. See tables.modes for all the possible + values. By default this parameter is set to 'H' which is the highest + possible error correction, but it has the smallest available data + capacity. + + The *version* parameter specifies the size and data capacity of the + code. Versions are any integer between 1 and 40. Where version 1 is + the smallest QR code, and version 40 is the largest. If this parameter + is left unspecified, then the contents and error correction level will + be used to guess the smallest possible QR code version that the + content will fit inside of. You may want to specify this parameter + for consistency when generating several QR codes with varying amounts + of data. That way all of the generated codes would have the same size. + + The *mode* parameter specifies how the contents will be encoded. By + default, the best possible mode for the contents is guessed. There + are four possible modes. First, is 'numeric' which is + used to encode integer numbers. Next, is 'alphanumeric' which is + used to encode some ASCII characters. This mode uses only a limited + set of characters. Most problematic is that it can only use upper case + English characters, consequently, the content parameter will be + subjected to str.upper() before encoding. See tables.ascii_codes for + a complete list of available characters. The is 'kanji' mode can be + used for Japanese characters, but only those that can be understood + via the shift-jis string encoding. Finally, we then have 'binary' mode + which just encodes the bytes directly into the QR code (this encoding + is the least efficient). + + The *encoding* parameter specifies how the content will be interpreted. + This parameter only matters if the *content* is a string, unicode, or + byte array type. This parameter must be a valid encoding string or None. + t will be passed the *content*'s encode/decode methods. + """ + return QRCode(content, error, version, mode, encoding) + +class QRCode: + """This class represents a QR code. To use this class simply give the + constructor a string representing the data to be encoded, it will then + build a code in memory. You can then save it in various formats. Note, + codes can be written out as PNG files but this requires the PyPNG module. + You can find the PyPNG module at http://packages.python.org/pypng/. + + Examples: + >>> from pyqrcode import QRCode + >>> import sys + >>> url = QRCode('http://uca.edu') + >>> url.svg(sys.stdout, scale=1) + >>> url.svg('uca.svg', scale=4) + >>> number = QRCode(123456789012345) + >>> number.png('big-number.png') + + .. note:: + For what all of the parameters do, see the :func:`pyqrcode.create` + function. + """ + def __init__(self, content, error='H', version=None, mode=None, + encoding='iso-8859-1'): + #Guess the mode of the code, this will also be used for + #error checking + guessed_content_type, encoding = self._detect_content_type(content, encoding) + + if encoding is None: + encoding = 'iso-8859-1' + + #Store the encoding for use later + if guessed_content_type == 'kanji': + self.encoding = 'shiftjis' + else: + self.encoding = encoding + + if version is not None: + if 1 <= version <= 40: + self.version = version + else: + raise ValueError("Illegal version {0}, version must be between " + "1 and 40.".format(version)) + + #Decode a 'byte array' contents into a string format + if isinstance(content, bytes): + self.data = content.decode(encoding) + + #Give a string an encoding + elif hasattr(content, 'encode'): + self.data = content.encode(self.encoding) + + #The contents are not a byte array or string, so + #try naively converting to a string representation. + else: + self.data = str(content) # str == unicode in Py 2.x, see file head + + #Force a passed in mode to be lowercase + if hasattr(mode, 'lower'): + mode = mode.lower() + + #Check that the mode parameter is compatible with the contents + if mode is None: + #Use the guessed mode + self.mode = guessed_content_type + self.mode_num = tables.modes[self.mode] + elif mode not in tables.modes.keys(): + #Unknown mode + raise ValueError('{0} is not a valid mode.'.format(mode)) + elif guessed_content_type == 'binary' and \ + tables.modes[mode] != tables.modes['binary']: + #Binary is only guessed as a last resort, if the + #passed in mode is not binary the data won't encode + raise ValueError('The content provided cannot be encoded with ' + 'the mode {}, it can only be encoded as ' + 'binary.'.format(mode)) + elif tables.modes[mode] == tables.modes['numeric'] and \ + guessed_content_type != 'numeric': + #If numeric encoding is requested make sure the data can + #be encoded in that format + raise ValueError('The content cannot be encoded as numeric.') + elif tables.modes[mode] == tables.modes['kanji'] and \ + guessed_content_type != 'kanji': + raise ValueError('The content cannot be encoded as kanji.') + else: + #The data should encode with the passed in mode + self.mode = mode + self.mode_num = tables.modes[self.mode] + + #Check that the user passed in a valid error level + if error in tables.error_level.keys(): + self.error = tables.error_level[error] + else: + raise ValueError('{0} is not a valid error ' + 'level.'.format(error)) + + #Guess the "best" version + self.version = self._pick_best_fit(self.data) + + #If the user supplied a version, then check that it has + #sufficient data capacity for the contents passed in + if version: + if version >= self.version: + self.version = version + else: + raise ValueError('The data will not fit inside a version {} ' + 'code with the given encoding and error ' + 'level (the code must be at least a ' + 'version {}).'.format(version, self.version)) + + #Build the QR code + self.builder = builder.QRCodeBuilder(data=self.data, + version=self.version, + mode=self.mode, + error=self.error) + + #Save the code for easier reference + self.code = self.builder.code + + def __str__(self): + return repr(self) + + def __unicode__(self): + return self.__repr__() + + def __repr__(self): + return "QRCode(content={0}, error='{1}', version={2}, mode='{3}')" \ + .format(repr(self.data), self.error, self.version, self.mode) + + def _detect_content_type(self, content, encoding): + """This method tries to auto-detect the type of the data. It first + tries to see if the data is a valid integer, in which case it returns + numeric. Next, it tests the data to see if it is 'alphanumeric.' QR + Codes use a special table with very limited range of ASCII characters. + The code's data is tested to make sure it fits inside this limited + range. If all else fails, the data is determined to be of type + 'binary.' + + Returns a tuple containing the detected mode and encoding. + + Note, encoding ECI is not yet implemented. + """ + def two_bytes(c): + """Output two byte character code as a single integer.""" + def next_byte(b): + """Make sure that character code is an int. Python 2 and + 3 compatibility. + """ + if not isinstance(b, int): + return ord(b) + else: + return b + + #Go through the data by looping to every other character + for i in range(0, len(c), 2): + yield (next_byte(c[i]) << 8) | next_byte(c[i+1]) + + #See if the data is a number + try: + if str(content).isdigit(): + return 'numeric', encoding + except (TypeError, UnicodeError): + pass + + #See if that data is alphanumeric based on the standards + #special ASCII table + valid_characters = ''.join(tables.ascii_codes.keys()) + + #Force the characters into a byte array + valid_characters = valid_characters.encode('ASCII') + + try: + if isinstance(content, bytes): + c = content.decode('ASCII') + else: + c = str(content).encode('ASCII') + + if all(map(lambda x: x in valid_characters, c)): + return 'alphanumeric', 'ASCII' + + #This occurs if the content does not contain ASCII characters. + #Since the whole point of the if statement is to look for ASCII + #characters, the resulting mode should not be alphanumeric. + #Hence, this is not an error. + except TypeError: + pass + except UnicodeError: + pass + + try: + if isinstance(content, bytes): + if encoding is None: + encoding = 'shiftjis' + + c = content.decode(encoding).encode('shiftjis') + else: + c = content.encode('shiftjis') + + #All kanji characters must be two bytes long, make sure the + #string length is not odd. + if len(c) % 2 != 0: + return 'binary', encoding + + #Make sure the characters are actually in range. + for asint in two_bytes(c): + #Shift the two byte value as indicated by the standard + if not (0x8140 <= asint <= 0x9FFC or + 0xE040 <= asint <= 0xEBBF): + return 'binary', encoding + + return 'kanji', encoding + + except UnicodeError: + #This occurs if the content does not contain Shift JIS kanji + #characters. Hence, the resulting mode should not be kanji. + #This is not an error. + pass + + #All of the other attempts failed. The content can only be binary. + return 'binary', encoding + + def _pick_best_fit(self, content): + """This method return the smallest possible QR code version number + that will fit the specified data with the given error level. + """ + import math + + for version in range(1, 41): + #Get the maximum possible capacity + capacity = tables.data_capacity[version][self.error][self.mode_num] + + #Check the capacity + #Kanji's count in the table is "characters" which are two bytes + if (self.mode_num == tables.modes['kanji'] and + capacity >= math.ceil(len(content) / 2)): + return version + if capacity >= len(content): + return version + + raise ValueError('The data will not fit in any QR code version ' + 'with the given encoding and error level.') + + def show(self, wait=1.2, scale=10, module_color=(0, 0, 0, 255), + background=(255, 255, 255, 255), quiet_zone=4): + """Displays this QR code. + + This method is mainly intended for debugging purposes. + + This method saves the output of the :py:meth:`png` method (with a default + scaling factor of 10) to a temporary file and opens it with the + standard PNG viewer application or within the standard webbrowser. The + temporary file is deleted afterwards. + + If this method does not show any result, try to increase the `wait` + parameter. This parameter specifies the time in seconds to wait till + the temporary file is deleted. Note, that this method does not return + until the provided amount of seconds (default: 1.2) has passed. + + The other parameters are simply passed on to the `png` method. + """ + import os + import time + import tempfile + import webbrowser + + try: # Python 2 + from urlparse import urljoin + from urllib import pathname2url + except ImportError: # Python 3 + from urllib.parse import urljoin + from urllib.request import pathname2url + + f = tempfile.NamedTemporaryFile('wb', suffix='.png', delete=False) + self.png(f, scale=scale, module_color=module_color, + background=background, quiet_zone=quiet_zone) + f.close() + webbrowser.open_new_tab(urljoin('file:', pathname2url(f.name))) + time.sleep(wait) + os.unlink(f.name) + + def get_png_size(self, scale=1, quiet_zone=4): + """This is method helps users determine what *scale* to use when + creating a PNG of this QR code. It is meant mostly to be used in the + console to help the user determine the pixel size of the code + using various scales. + + This method will return an integer representing the width and height of + the QR code in pixels, as if it was drawn using the given *scale*. + Because QR codes are square, the number represents both the width + and height dimensions. + + The *quiet_zone* parameter sets how wide the quiet zone around the code + should be. According to the standard this should be 4 modules. It is + left settable because such a wide quiet zone is unnecessary in many + applications where the QR code is not being printed. + + Example: + >>> code = pyqrcode.QRCode("I don't like spam!") + >>> print(code.get_png_size(1)) + 31 + >>> print(code.get_png_size(5)) + 155 + """ + return builder._get_png_size(self.version, scale, quiet_zone) + + def png(self, file, scale=1, module_color=(0, 0, 0, 255), + background=(255, 255, 255, 255), quiet_zone=4): + """This method writes the QR code out as an PNG image. The resulting + PNG has a bit depth of 1. The file parameter is used to specify where + to write the image to. It can either be an writable stream or a + file path. + + .. note:: + This method depends on the pypng module to actually create the + PNG file. + + This method will write the given *file* out as a PNG file. The file + can be either a string file path, or a writable stream. The file + will not be automatically closed if a stream is given. + + The *scale* parameter sets how large to draw a single module. By + default one pixel is used to draw a single module. This may make the + code too small to be read efficiently. Increasing the scale will make + the code larger. Only integer scales are usable. This method will + attempt to coerce the parameter into an integer (e.g. 2.5 will become 2, + and '3' will become 3). You can use the :py:meth:`get_png_size` method + to calculate the actual pixel size of the resulting PNG image. + + The *module_color* parameter sets what color to use for the encoded + modules (the black part on most QR codes). The *background* parameter + sets what color to use for the background (the white part on most + QR codes). If either parameter is set, then both must be + set or a ValueError is raised. Colors should be specified as either + a list or a tuple of length 3 or 4. The components of the list must + be integers between 0 and 255. The first three member give the RGB + color. The fourth member gives the alpha component, where 0 is + transparent and 255 is opaque. Note, many color + combinations are unreadable by scanners, so be judicious. + + The *quiet_zone* parameter sets how wide the quiet zone around the code + should be. According to the standard this should be 4 modules. It is + left settable because such a wide quiet zone is unnecessary in many + applications where the QR code is not being printed. + + Example: + >>> code = pyqrcode.create('Are you suggesting coconuts migrate?') + >>> code.png('swallow.png', scale=5) + >>> code.png('swallow.png', scale=5, + module_color=(0x66, 0x33, 0x0), #Dark brown + background=(0xff, 0xff, 0xff, 0x88)) #50% transparent white + """ + builder._png(self.code, self.version, file, scale, + module_color, background, quiet_zone) + + def png_as_base64_str(self, scale=1, module_color=(0, 0, 0, 255), + background=(255, 255, 255, 255), quiet_zone=4): + """This method uses the png render and returns the PNG image encoded as + base64 string. This can be useful for creating dynamic PNG images for + web development, since no file needs to be created. + + Example: + >>> code = pyqrcode.create('Are you suggesting coconuts migrate?') + >>> image_as_str = code.png_as_base64_str(scale=5) + >>> html_img = ''.format(image_as_str) + + The parameters are passed directly to the :py:meth:`png` method. Refer + to that method's documentation for the meaning behind the parameters. + + .. note:: + This method depends on the pypng module to actually create the + PNG image. + + """ + import io + import base64 + + with io.BytesIO() as virtual_file: + self.png(file=virtual_file, scale=scale, module_color=module_color, + background=background, quiet_zone=quiet_zone) + image_as_str = base64.b64encode(virtual_file.getvalue()).decode("ascii") + return image_as_str + + def xbm(self, scale=1, quiet_zone=4): + """Returns a string representing an XBM image of the QR code. + The XBM format is a black and white image format that looks like a + C header file. + + Because displaying QR codes in Tkinter is the + primary use case for this renderer, this method does not take a file + parameter. Instead it retuns the rendered QR code data as a string. + + Example of using this renderer with Tkinter: + >>> import pyqrcode + >>> import tkinter + >>> code = pyqrcode.create('Knights who say ni!') + >>> code_xbm = code.xbm(scale=5) + >>> + >>> top = tkinter.Tk() + >>> code_bmp = tkinter.BitmapImage(data=code_xbm) + >>> code_bmp.config(foreground="black") + >>> code_bmp.config(background="white") + >>> label = tkinter.Label(image=code_bmp) + >>> label.pack() + + + The *scale* parameter sets how large to draw a single module. By + default one pixel is used to draw a single module. This may make the + code too small to be read efficiently. Increasing the scale will make + the code larger. Only integer scales are usable. This method will + attempt to coerce the parameter into an integer (e.g. 2.5 will become 2, + and '3' will become 3). You can use the :py:meth:`get_png_size` method + to calculate the actual pixel size of this image when displayed. + + The *quiet_zone* parameter sets how wide the quiet zone around the code + should be. According to the standard this should be 4 modules. It is + left settable because such a wide quiet zone is unnecessary in many + applications where the QR code is not being printed. + """ + return builder._xbm(self.code, scale, quiet_zone) + + def svg(self, file, scale=1, module_color='#000', background=None, + quiet_zone=4, xmldecl=True, svgns=True, title=None, + svgclass='pyqrcode', lineclass='pyqrline', omithw=False, + debug=False): + """This method writes the QR code out as an SVG document. The + code is drawn by drawing only the modules corresponding to a 1. They + are drawn using a line, such that contiguous modules in a row + are drawn with a single line. + + The *file* parameter is used to specify where to write the document + to. It can either be a writable stream or a file path. + + The *scale* parameter sets how large to draw + a single module. By default one pixel is used to draw a single + module. This may make the code too small to be read efficiently. + Increasing the scale will make the code larger. Unlike the png() method, + this method will accept fractional scales (e.g. 2.5). + + Note, three things are done to make the code more appropriate for + embedding in a HTML document. The "white" part of the code is actually + transparent. The code itself has a class given by *svgclass* parameter. + The path making up the QR code uses the class set using the *lineclass*. + These should make the code easier to style using CSS. + + By default the output of this function is a complete SVG document. If + only the code itself is desired, set the *xmldecl* to false. This will + result in a fragment that contains only the "drawn" portion of the code. + Likewise, you can set the *title* of the document. The SVG name space + attribute can be suppressed by setting *svgns* to False. + + When True the *omithw* indicates if width and height attributes should + be omitted. If these attributes are omitted, a ``viewBox`` attribute + will be added to the document. + + You can also set the colors directly using the *module_color* and + *background* parameters. The *module_color* parameter sets what color to + use for the data modules (the black part on most QR codes). The + *background* parameter sets what color to use for the background (the + white part on most QR codes). The parameters can be set to any valid + SVG or HTML color. If the background is set to None, then no background + will be drawn, i.e. the background will be transparent. Note, many color + combinations are unreadable by scanners, so be careful. + + The *quiet_zone* parameter sets how wide the quiet zone around the code + should be. According to the standard this should be 4 modules. It is + left settable because such a wide quiet zone is unnecessary in many + applications where the QR code is not being printed. + + Example: + >>> code = pyqrcode.create('Hello. Uhh, can we have your liver?') + >>> code.svg('live-organ-transplants.svg', 3.6) + >>> code.svg('live-organ-transplants.svg', scale=4, + module_color='brown', background='0xFFFFFF') + """ + builder._svg(self.code, self.version, file, scale=scale, + module_color=module_color, background=background, + quiet_zone=quiet_zone, xmldecl=xmldecl, svgns=svgns, + title=title, svgclass=svgclass, lineclass=lineclass, + omithw=omithw, debug=debug) + + def eps(self, file, scale=1, module_color=(0, 0, 0), + background=None, quiet_zone=4): + """This method writes the QR code out as an EPS document. The + code is drawn by only writing the data modules corresponding to a 1. + They are drawn using a line, such that contiguous modules in a row + are drawn with a single line. + + The *file* parameter is used to specify where to write the document + to. It can either be a writable (text) stream or a file path. + + The *scale* parameter sets how large to draw a single module. By + default one point (1/72 inch) is used to draw a single module. This may + make the code to small to be read efficiently. Increasing the scale + will make the code larger. This method will accept fractional scales + (e.g. 2.5). + + The *module_color* parameter sets the color of the data modules. The + *background* parameter sets the background (page) color to use. They + are specified as either a triple of floats, e.g. (0.5, 0.5, 0.5), or a + triple of integers, e.g. (128, 128, 128). The default *module_color* is + black. The default *background* color is no background at all. + + The *quiet_zone* parameter sets how large to draw the border around + the code. As per the standard, the default value is 4 modules. + + Examples: + >>> qr = pyqrcode.create('Hello world') + >>> qr.eps('hello-world.eps', scale=2.5, module_color='#36C') + >>> qr.eps('hello-world2.eps', background='#eee') + >>> out = io.StringIO() + >>> qr.eps(out, module_color=(.4, .4, .4)) + """ + builder._eps(self.code, self.version, file, scale, module_color, + background, quiet_zone) + + def terminal(self, module_color='default', background='reverse', + quiet_zone=4): + """This method returns a string containing ASCII escape codes, + such that if printed to a compatible terminal, it will display + a vaild QR code. The code is printed using ASCII escape + codes that alter the coloring of the background. + + The *module_color* parameter sets what color to + use for the data modules (the black part on most QR codes). + Likewise, the *background* parameter sets what color to use + for the background (the white part on most QR codes). + + There are two options for colors. The first, and most widely + supported, is to use the 8 or 16 color scheme. This scheme uses + eight to sixteen named colors. The following colors are + supported the most widely supported: black, red, green, + yellow, blue, magenta, and cyan. There are an some additional + named colors that are supported by most terminals: light gray, + dark gray, light red, light green, light blue, light yellow, + light magenta, light cyan, and white. + + There are two special named colors. The first is the + "default" color. This color is the color the background of + the terminal is set to. The next color is the "reverse" + color. This is not really a color at all but a special + property that will reverse the current color. These two colors + are the default values for *module_color* and *background* + respectively. These values should work on most terminals. + + Finally, there is one more way to specify the color. Some + terminals support 256 colors. The actual colors displayed in the + terminal is system dependent. This is the least transportable option. + To use the 256 color scheme set *module_color* and/or + *background* to a number between 0 and 256. + + The *quiet_zone* parameter sets how wide the quiet zone around the code + should be. According to the standard this should be 4 modules. It is + left settable because such a wide quiet zone is unnecessary in many + applications. + + Example: + >>> code = pyqrcode.create('Example') + >>> text = code.terminal() + >>> print(text) + """ + return builder._terminal(self.code, module_color, background, + quiet_zone) + + def text(self, quiet_zone=4): + """This method returns a string based representation of the QR code. + The data modules are represented by 1's and the background modules are + represented by 0's. The main purpose of this method is to act a + starting point for users to create their own renderers. + + The *quiet_zone* parameter sets how wide the quiet zone around the code + should be. According to the standard this should be 4 modules. It is + left settable because such a wide quiet zone is unnecessary in many + applications. + + Example: + >>> code = pyqrcode.create('Example') + >>> text = code.text() + >>> print(text) + """ + return builder._text(self.code, quiet_zone) + diff --git a/pyqrcode/builder.py b/pyqrcode/builder.py new file mode 100644 index 0000000..7313d5a --- /dev/null +++ b/pyqrcode/builder.py @@ -0,0 +1,1528 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2013, Michael Nooner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +"""This module does the actual generation of the QR codes. The QRCodeBuilder +builds the code. While the various output methods draw the code into a file. +""" + +#Imports required for 2.x support +from __future__ import absolute_import, division, print_function, with_statement, unicode_literals + +import pyqrcode.tables as tables +import io +import itertools +import math + +class QRCodeBuilder: + """This class generates a QR code based on the standard. It is meant to + be used internally, not by users!!! + + This class implements the tutorials found at: + + * http://www.thonky.com/qr-code-tutorial/ + + * http://www.matchadesign.com/blog/qr-code-demystified-part-6/ + + This class also uses the standard, which can be read online at: + http://raidenii.net/files/datasheets/misc/qr_code.pdf + + Test codes were tested against: + http://zxing.org/w/decode.jspx + + Also, reference codes were generat/ed at: + http://www.morovia.com/free-online-barcode-generator/qrcode-maker.php + http://demos.telerik.com/aspnet-ajax/barcode/examples/qrcode/defaultcs.aspx + + QR code Debugger: + http://qrlogo.kaarposoft.dk/qrdecode.html + """ + def __init__(self, data, version, mode, error): + """See :py:class:`pyqrcode.QRCode` for information on the parameters.""" + #Set what data we are going to use to generate + #the QR code + self.data = data + + #Check that the user passed in a valid mode + if mode in tables.modes: + self.mode = tables.modes[mode] + else: + raise ValueError('{0} is not a valid mode.'.format(mode)) + + #Check that the user passed in a valid error level + if error in tables.error_level: + self.error = tables.error_level[error] + else: + raise ValueError('{0} is not a valid error ' + 'level.'.format(error)) + + if 1 <= version <= 40: + self.version = version + else: + raise ValueError("Illegal version {0}, version must be between " + "1 and 40.".format(version)) + + #Look up the proper row for error correction code words + self.error_code_words = tables.eccwbi[version][self.error] + + #This property will hold the binary string as it is built + self.buffer = io.StringIO() + + #Create the binary data block + self.add_data() + + #Create the actual QR code + self.make_code() + + def grouper(self, n, iterable, fillvalue=None): + """This generator yields a set of tuples, where the + iterable is broken into n sized chunks. If the + iterable is not evenly sized then fillvalue will + be appended to the last tuple to make up the difference. + + This function is copied from the standard docs on + itertools. + """ + args = [iter(iterable)] * n + if hasattr(itertools, 'zip_longest'): + return itertools.zip_longest(*args, fillvalue=fillvalue) + return itertools.izip_longest(*args, fillvalue=fillvalue) + + def binary_string(self, data, length): + """This method returns a string of length n that is the binary + representation of the given data. This function is used to + basically create bit fields of a given size. + """ + return '{{0:0{0}b}}'.format(length).format(int(data)) + + def get_data_length(self): + """QR codes contain a "data length" field. This method creates this + field. A binary string representing the appropriate length is + returned. + """ + + #The "data length" field varies by the type of code and its mode. + #discover how long the "data length" field should be. + if 1 <= self.version <= 9: + max_version = 9 + elif 10 <= self.version <= 26: + max_version = 26 + elif 27 <= self.version <= 40: + max_version = 40 + + data_length = tables.data_length_field[max_version][self.mode] + + if self.mode != tables.modes['kanji']: + length_string = self.binary_string(len(self.data), data_length) + else: + length_string = self.binary_string(len(self.data) / 2, data_length) + + if len(length_string) > data_length: + raise ValueError('The supplied data will not fit ' + 'within this version of a QRCode.') + return length_string + + def encode(self): + """This method encodes the data into a binary string using + the appropriate algorithm specified by the mode. + """ + if self.mode == tables.modes['alphanumeric']: + encoded = self.encode_alphanumeric() + elif self.mode == tables.modes['numeric']: + encoded = self.encode_numeric() + elif self.mode == tables.modes['binary']: + encoded = self.encode_bytes() + elif self.mode == tables.modes['kanji']: + encoded = self.encode_kanji() + return encoded + + def encode_alphanumeric(self): + """This method encodes the QR code's data if its mode is + alphanumeric. It returns the data encoded as a binary string. + """ + #Convert the string to upper case + self.data = self.data.upper() + + #Change the data such that it uses a QR code ascii table + ascii = [] + for char in self.data: + if isinstance(char, int): + ascii.append(tables.ascii_codes[chr(char)]) + else: + ascii.append(tables.ascii_codes[char]) + + #Now perform the algorithm that will make the ascii into bit fields + with io.StringIO() as buf: + for (a,b) in self.grouper(2, ascii): + if b is not None: + buf.write(self.binary_string((45*a)+b, 11)) + else: + #This occurs when there is an odd number + #of characters in the data + buf.write(self.binary_string(a, 6)) + + #Return the binary string + return buf.getvalue() + + def encode_numeric(self): + """This method encodes the QR code's data if its mode is + numeric. It returns the data encoded as a binary string. + """ + with io.StringIO() as buf: + #Break the number into groups of three digits + for triplet in self.grouper(3, self.data): + number = '' + for digit in triplet: + if isinstance(digit, int): + digit = chr(digit) + + #Only build the string if digit is not None + if digit: + number = ''.join([number, digit]) + else: + break + + #If the number is one digits, make a 4 bit field + if len(number) == 1: + bin = self.binary_string(number, 4) + + #If the number is two digits, make a 7 bit field + elif len(number) == 2: + bin = self.binary_string(number, 7) + + #Three digit numbers use a 10 bit field + else: + bin = self.binary_string(number, 10) + + buf.write(bin) + return buf.getvalue() + + def encode_bytes(self): + """This method encodes the QR code's data if its mode is + 8 bit mode. It returns the data encoded as a binary string. + """ + with io.StringIO() as buf: + for char in self.data: + if not isinstance(char, int): + buf.write('{{0:0{0}b}}'.format(8).format(ord(char))) + else: + buf.write('{{0:0{0}b}}'.format(8).format(char)) + return buf.getvalue() + + def encode_kanji(self): + """This method encodes the QR code's data if its mode is + kanji. It returns the data encoded as a binary string. + """ + def two_bytes(data): + """Output two byte character code as a single integer.""" + def next_byte(b): + """Make sure that character code is an int. Python 2 and + 3 compatibility. + """ + if not isinstance(b, int): + return ord(b) + else: + return b + + #Go through the data by looping to every other character + for i in range(0, len(data), 2): + yield (next_byte(data[i]) << 8) | next_byte(data[i+1]) + + #Force the data into Kanji encoded bytes + if isinstance(self.data, bytes): + data = self.data.decode('shiftjis').encode('shiftjis') + else: + data = self.data.encode('shiftjis') + + #Now perform the algorithm that will make the kanji into 13 bit fields + with io.StringIO() as buf: + for asint in two_bytes(data): + #Shift the two byte value as indicated by the standard + if 0x8140 <= asint <= 0x9FFC: + difference = asint - 0x8140 + elif 0xE040 <= asint <= 0xEBBF: + difference = asint - 0xC140 + + #Split the new value into most and least significant bytes + msb = (difference >> 8) + lsb = (difference & 0x00FF) + + #Calculate the actual 13 bit binary value + buf.write('{0:013b}'.format((msb * 0xC0) + lsb)) + #Return the binary string + return buf.getvalue() + + + def add_data(self): + """This function properly constructs a QR code's data string. It takes + into account the interleaving pattern required by the standard. + """ + #Encode the data into a QR code + self.buffer.write(self.binary_string(self.mode, 4)) + self.buffer.write(self.get_data_length()) + self.buffer.write(self.encode()) + + #Converts the buffer into "code word" integers. + #The online debugger outputs them this way, makes + #for easier comparisons. + #s = self.buffer.getvalue() + #for i in range(0, len(s), 8): + # print(int(s[i:i+8], 2), end=',') + #print() + + #Fix for issue #3: https://github.com/mnooner256/pyqrcode/issues/3# + #I was performing the terminate_bits() part in the encoding. + #As per the standard, terminating bits are only supposed to + #be added after the bit stream is complete. I took that to + #mean after the encoding, but actually it is after the entire + #bit stream has been constructed. + bits = self.terminate_bits(self.buffer.getvalue()) + if bits is not None: + self.buffer.write(bits) + + #delimit_words and add_words can return None + add_bits = self.delimit_words() + if add_bits: + self.buffer.write(add_bits) + + fill_bytes = self.add_words() + if fill_bytes: + self.buffer.write(fill_bytes) + + #Get a numeric representation of the data + data = [int(''.join(x),2) + for x in self.grouper(8, self.buffer.getvalue())] + + #This is the error information for the code + error_info = tables.eccwbi[self.version][self.error] + + #This will hold our data blocks + data_blocks = [] + + #This will hold our error blocks + error_blocks = [] + + #Some codes have the data sliced into two different sized blocks + #for example, first two 14 word sized blocks, then four 15 word + #sized blocks. This means that slicing size can change over time. + data_block_sizes = [error_info[2]] * error_info[1] + if error_info[3] != 0: + data_block_sizes.extend([error_info[4]] * error_info[3]) + + #For every block of data, slice the data into the appropriate + #sized block + current_byte = 0 + for n_data_blocks in data_block_sizes: + data_blocks.append(data[current_byte:current_byte+n_data_blocks]) + current_byte += n_data_blocks + + #I am not sure about the test after the "and". This was added to + #fix a bug where after delimit_words padded the bit stream, a zero + #byte ends up being added. After checking around, it seems this extra + #byte is supposed to be chopped off, but I cannot find that in the + #standard! I am adding it to solve the bug, I believe it is correct. + if current_byte < len(data): + raise ValueError('Too much data for this code version.') + + #DEBUG CODE!!!! + #Print out the data blocks + #print('Data Blocks:\n{0}'.format(data_blocks)) + + #Calculate the error blocks + for n, block in enumerate(data_blocks): + error_blocks.append(self.make_error_block(block, n)) + + #DEBUG CODE!!!! + #Print out the error blocks + #print('Error Blocks:\n{0}'.format(error_blocks)) + + #Buffer we will write our data blocks into + data_buffer = io.StringIO() + + #Add the data blocks + #Write the buffer such that: block 1 byte 1, block 2 byte 1, etc. + largest_block = max(error_info[2], error_info[4])+error_info[0] + for i in range(largest_block): + for block in data_blocks: + if i < len(block): + data_buffer.write(self.binary_string(block[i], 8)) + + #Add the error code blocks. + #Write the buffer such that: block 1 byte 1, block 2 byte 2, etc. + for i in range(error_info[0]): + for block in error_blocks: + data_buffer.write(self.binary_string(block[i], 8)) + + self.buffer = data_buffer + + def terminate_bits(self, payload): + """This method adds zeros to the end of the encoded data so that the + encoded data is of the correct length. It returns a binary string + containing the bits to be added. + """ + data_capacity = tables.data_capacity[self.version][self.error][0] + + if len(payload) > data_capacity: + raise ValueError('The supplied data will not fit ' + 'within this version of a QR code.') + + #We must add up to 4 zeros to make up for any shortfall in the + #length of the data field. + if len(payload) == data_capacity: + return None + elif len(payload) <= data_capacity-4: + bits = self.binary_string(0,4) + else: + #Make up any shortfall need with less than 4 zeros + bits = self.binary_string(0, data_capacity - len(payload)) + + return bits + + def delimit_words(self): + """This method takes the existing encoded binary string + and returns a binary string that will pad it such that + the encoded string contains only full bytes. + """ + bits_short = 8 - (len(self.buffer.getvalue()) % 8) + + #The string already falls on an byte boundary do nothing + if bits_short == 0 or bits_short == 8: + return None + else: + return self.binary_string(0, bits_short) + + def add_words(self): + """The data block must fill the entire data capacity of the QR code. + If we fall short, then we must add bytes to the end of the encoded + data field. The value of these bytes are specified in the standard. + """ + + data_blocks = len(self.buffer.getvalue()) // 8 + total_blocks = tables.data_capacity[self.version][self.error][0] // 8 + needed_blocks = total_blocks - data_blocks + + if needed_blocks == 0: + return None + + #This will return item1, item2, item1, item2, etc. + block = itertools.cycle(['11101100', '00010001']) + + #Create a string of the needed blocks + return ''.join([next(block) for x in range(needed_blocks)]) + + def make_error_block(self, block, block_number): + """This function constructs the error correction block of the + given data block. This is *very complicated* process. To + understand the code you need to read: + + * http://www.thonky.com/qr-code-tutorial/part-2-error-correction/ + * http://www.matchadesign.com/blog/qr-code-demystified-part-4/ + """ + #Get the error information from the standards table + error_info = tables.eccwbi[self.version][self.error] + + #This is the number of 8-bit words per block + if block_number < error_info[1]: + code_words_per_block = error_info[2] + else: + code_words_per_block = error_info[4] + + #This is the size of the error block + error_block_size = error_info[0] + + #Copy the block as the message polynomial coefficients + mp_co = block[:] + + #Add the error blocks to the message polynomial + mp_co.extend([0] * (error_block_size)) + + #Get the generator polynomial + generator = tables.generator_polynomials[error_block_size] + + #This will hold the temporary sum of the message coefficient and the + #generator polynomial + gen_result = [0] * len(generator) + + #Go through every code word in the block + for i in range(code_words_per_block): + #Get the first coefficient from the message polynomial + coefficient = mp_co.pop(0) + + #Skip coefficients that are zero + if coefficient == 0: + continue + else: + #Turn the coefficient into an alpha exponent + alpha_exp = tables.galois_antilog[coefficient] + + #Add the alpha to the generator polynomial + for n in range(len(generator)): + gen_result[n] = alpha_exp + generator[n] + if gen_result[n] > 255: + gen_result[n] = gen_result[n] % 255 + + #Convert the alpha notation back into coefficients + gen_result[n] = tables.galois_log[gen_result[n]] + + #XOR the sum with the message coefficients + mp_co[n] = gen_result[n] ^ mp_co[n] + + #Pad the end of the error blocks with zeros if needed + if len(mp_co) < code_words_per_block: + mp_co.extend([0] * (code_words_per_block - len(mp_co))) + + return mp_co + + def make_code(self): + """This method returns the best possible QR code.""" + from copy import deepcopy + + #Get the size of the underlying matrix + matrix_size = tables.version_size[self.version] + + #Create a template matrix we will build the codes with + row = [' ' for x in range(matrix_size)] + template = [deepcopy(row) for x in range(matrix_size)] + + #Add mandatory information to the template + self.add_detection_pattern(template) + self.add_position_pattern(template) + self.add_version_pattern(template) + + #Create the various types of masks of the template + self.masks = self.make_masks(template) + + self.best_mask = self.choose_best_mask() + self.code = self.masks[self.best_mask] + + def add_detection_pattern(self, m): + """This method add the detection patterns to the QR code. This lets + the scanner orient the pattern. It is required for all QR codes. + The detection pattern consists of three boxes located at the upper + left, upper right, and lower left corners of the matrix. Also, two + special lines called the timing pattern is also necessary. Finally, + a single black pixel is added just above the lower left black box. + """ + + #Draw outer black box + for i in range(7): + inv = -(i+1) + for j in [0,6,-1,-7]: + m[j][i] = 1 + m[i][j] = 1 + m[inv][j] = 1 + m[j][inv] = 1 + + #Draw inner white box + for i in range(1, 6): + inv = -(i+1) + for j in [1, 5, -2, -6]: + m[j][i] = 0 + m[i][j] = 0 + m[inv][j] = 0 + m[j][inv] = 0 + + #Draw inner black box + for i in range(2, 5): + for j in range(2, 5): + inv = -(i+1) + m[i][j] = 1 + m[inv][j] = 1 + m[j][inv] = 1 + + #Draw white border + for i in range(8): + inv = -(i+1) + for j in [7, -8]: + m[i][j] = 0 + m[j][i] = 0 + m[inv][j] = 0 + m[j][inv] = 0 + + #To keep the code short, it draws an extra box + #in the lower right corner, this removes it. + for i in range(-8, 0): + for j in range(-8, 0): + m[i][j] = ' ' + + #Add the timing pattern + bit = itertools.cycle([1,0]) + for i in range(8, (len(m)-8)): + b = next(bit) + m[i][6] = b + m[6][i] = b + + #Add the extra black pixel + m[-8][8] = 1 + + def add_position_pattern(self, m): + """This method draws the position adjustment patterns onto the QR + Code. All QR code versions larger than one require these special boxes + called position adjustment patterns. + """ + #Version 1 does not have a position adjustment pattern + if self.version == 1: + return + + #Get the coordinates for where to place the boxes + coordinates = tables.position_adjustment[self.version] + + #Get the max and min coordinates to handle special cases + min_coord = coordinates[0] + max_coord = coordinates[-1] + + #Draw a box at each intersection of the coordinates + for i in coordinates: + for j in coordinates: + #Do not draw these boxes because they would + #interfere with the detection pattern + if (i == min_coord and j == min_coord) or \ + (i == min_coord and j == max_coord) or \ + (i == max_coord and j == min_coord): + continue + + #Center black pixel + m[i][j] = 1 + + #Surround the pixel with a white box + for x in [-1,1]: + m[i+x][j+x] = 0 + m[i+x][j] = 0 + m[i][j+x] = 0 + m[i-x][j+x] = 0 + m[i+x][j-x] = 0 + + #Surround the white box with a black box + for x in [-2,2]: + for y in [0,-1,1]: + m[i+x][j+x] = 1 + m[i+x][j+y] = 1 + m[i+y][j+x] = 1 + m[i-x][j+x] = 1 + m[i+x][j-x] = 1 + + def add_version_pattern(self, m): + """For QR codes with a version 7 or higher, a special pattern + specifying the code's version is required. + + For further information see: + http://www.thonky.com/qr-code-tutorial/format-version-information/#example-of-version-7-information-string + """ + if self.version < 7: + return + + #Get the bit fields for this code's version + #We will iterate across the string, the bit string + #needs the least significant digit in the zero-th position + field = iter(tables.version_pattern[self.version][::-1]) + + #Where to start placing the pattern + start = len(m)-11 + + #The version pattern is pretty odd looking + for i in range(6): + #The pattern is three modules wide + for j in range(start, start+3): + bit = int(next(field)) + + #Bottom Left + m[i][j] = bit + + #Upper right + m[j][i] = bit + + def make_masks(self, template): + """This method generates all seven masks so that the best mask can + be determined. The template parameter is a code matrix that will + server as the base for all the generated masks. + """ + from copy import deepcopy + + nmasks = len(tables.mask_patterns) + masks = [''] * nmasks + count = 0 + + for n in range(nmasks): + cur_mask = deepcopy(template) + masks[n] = cur_mask + + #Add the type pattern bits to the code + self.add_type_pattern(cur_mask, tables.type_bits[self.error][n]) + + #Get the mask pattern + pattern = tables.mask_patterns[n] + + #This will read the 1's and 0's one at a time + bits = iter(self.buffer.getvalue()) + + #These will help us do the up, down, up, down pattern + row_start = itertools.cycle([len(cur_mask)-1, 0]) + row_stop = itertools.cycle([-1,len(cur_mask)]) + direction = itertools.cycle([-1, 1]) + + #The data pattern is added using pairs of columns + for column in range(len(cur_mask)-1, 0, -2): + + #The vertical timing pattern is an exception to the rules, + #move the column counter over by one + if column <= 6: + column = column - 1 + + #This will let us fill in the pattern + #right-left, right-left, etc. + column_pair = itertools.cycle([column, column-1]) + + #Go through each row in the pattern moving up, then down + for row in range(next(row_start), next(row_stop), + next(direction)): + + #Fill in the right then left column + for i in range(2): + col = next(column_pair) + + #Go to the next column if we encounter a + #preexisting pattern (usually an alignment pattern) + if cur_mask[row][col] != ' ': + continue + + #Some versions don't have enough bits. You then fill + #in the rest of the pattern with 0's. These are + #called "remainder bits." + try: + bit = int(next(bits)) + except: + bit = 0 + + + #If the pattern is True then flip the bit + if pattern(row, col): + cur_mask[row][col] = bit ^ 1 + else: + cur_mask[row][col] = bit + + #DEBUG CODE!!! + #Save all of the masks as png files + #for i, m in enumerate(masks): + # _png(m, self.version, 'mask-{0}.png'.format(i), 5) + + return masks + + def choose_best_mask(self): + """This method returns the index of the "best" mask as defined by + having the lowest total penalty score. The penalty rules are defined + by the standard. The mask with the lowest total score should be the + easiest to read by optical scanners. + """ + self.scores = [] + for n in range(len(self.masks)): + self.scores.append([0,0,0,0]) + + #Score penalty rule number 1 + #Look for five consecutive squares with the same color. + #Each one found gets a penalty of 3 + 1 for every + #same color square after the first five in the row. + for (n, mask) in enumerate(self.masks): + current = mask[0][0] + counter = 0 + total = 0 + + #Examine the mask row wise + for row in range(0,len(mask)): + counter = 0 + for col in range(0,len(mask)): + bit = mask[row][col] + + if bit == current: + counter += 1 + else: + if counter >= 5: + total += (counter - 5) + 3 + counter = 1 + current = bit + if counter >= 5: + total += (counter - 5) + 3 + + #Examine the mask column wise + for col in range(0,len(mask)): + counter = 0 + for row in range(0,len(mask)): + bit = mask[row][col] + + if bit == current: + counter += 1 + else: + if counter >= 5: + total += (counter - 5) + 3 + counter = 1 + current = bit + if counter >= 5: + total += (counter - 5) + 3 + + self.scores[n][0] = total + + #Score penalty rule 2 + #This rule will add 3 to the score for each 2x2 block of the same + #colored pixels there are. + for (n, mask) in enumerate(self.masks): + count = 0 + #Don't examine the 0th and Nth row/column + for i in range(0, len(mask)-1): + for j in range(0, len(mask)-1): + if mask[i][j] == mask[i+1][j] and \ + mask[i][j] == mask[i][j+1] and \ + mask[i][j] == mask[i+1][j+1]: + count += 1 + + self.scores[n][1] = count * 3 + + #Score penalty rule 3 + #This rule looks for 1011101 within the mask prefixed + #and/or suffixed by four zeros. + patterns = [[0,0,0,0,1,0,1,1,1,0,1], + [1,0,1,1,1,0,1,0,0,0,0],] + #[0,0,0,0,1,0,1,1,1,0,1,0,0,0,0]] + + for (n, mask) in enumerate(self.masks): + nmatches = 0 + + for i in range(len(mask)): + for j in range(len(mask)): + for pattern in patterns: + match = True + k = j + #Look for row matches + for p in pattern: + if k >= len(mask) or mask[i][k] != p: + match = False + break + k += 1 + if match: + nmatches += 1 + + match = True + k = j + #Look for column matches + for p in pattern: + if k >= len(mask) or mask[k][i] != p: + match = False + break + k += 1 + if match: + nmatches += 1 + + + self.scores[n][2] = nmatches * 40 + + #Score the last rule, penalty rule 4. This rule measures how close + #the pattern is to being 50% black. The further it deviates from + #this this ideal the higher the penalty. + for (n, mask) in enumerate(self.masks): + nblack = 0 + for row in mask: + nblack += sum(row) + + total_pixels = len(mask)**2 + ratio = nblack / total_pixels + percent = (ratio * 100) - 50 + self.scores[n][3] = int((abs(int(percent)) / 5) * 10) + + + #Calculate the total for each score + totals = [0] * len(self.scores) + for i in range(len(self.scores)): + for j in range(len(self.scores[i])): + totals[i] += self.scores[i][j] + + #DEBUG CODE!!! + #Prints out a table of scores + #print('Rule Scores\n 1 2 3 4 Total') + #for i in range(len(self.scores)): + # print(i, end='') + # for s in self.scores[i]: + # print('{0: >6}'.format(s), end='') + # print('{0: >7}'.format(totals[i])) + #print('Mask Chosen: {0}'.format(totals.index(min(totals)))) + + #The lowest total wins + return totals.index(min(totals)) + + def add_type_pattern(self, m, type_bits): + """This will add the pattern to the QR code that represents the error + level and the type of mask used to make the code. + """ + field = iter(type_bits) + for i in range(7): + bit = int(next(field)) + + #Skip the timing bits + if i < 6: + m[8][i] = bit + else: + m[8][i+1] = bit + + if -8 < -(i+1): + m[-(i+1)][8] = bit + + for i in range(-8,0): + bit = int(next(field)) + + m[8][i] = bit + + i = -i + #Skip timing column + if i > 6: + m[i][8] = bit + else: + m[i-1][8] = bit + +############################################################################## +############################################################################## +# +# Output Functions +# +############################################################################## +############################################################################## + +def _get_writable(stream_or_path, mode): + """This method returns a tuple containing the stream and a flag to indicate + if the stream should be automatically closed. + + The `stream_or_path` parameter is returned if it is an open writable stream. + Otherwise, it treats the `stream_or_path` parameter as a file path and + opens it with the given mode. + + It is used by the svg and png methods to interpret the file parameter. + + :type stream_or_path: str | io.BufferedIOBase + :type mode: str | unicode + :rtype: (io.BufferedIOBase, bool) + """ + is_stream = hasattr(stream_or_path, 'write') + if not is_stream: + # No stream provided, treat "stream_or_path" as path + stream_or_path = open(stream_or_path, mode) + return stream_or_path, not is_stream + + +def _get_png_size(version, scale, quiet_zone=4): + """See: QRCode.get_png_size + + This function was abstracted away from QRCode to allow for the output of + QR codes during the build process, i.e. for debugging. It works + just the same except you must specify the code's version. This is needed + to calculate the PNG's size. + """ + #Formula: scale times number of modules plus the border on each side + return (int(scale) * tables.version_size[version]) + (2 * quiet_zone * int(scale)) + + +def _terminal(code, module_color='default', background='reverse', quiet_zone=4): + """This method returns a string containing ASCII escape codes, + such that if printed to a terminal, it will display a vaild + QR code. The module_color and the background color should be keys + in the tables.term_colors table for printing using the 8/16 + color scheme. Alternatively, they can be a number between 0 and + 256 in order to use the 88/256 color scheme. Otherwise, a + ValueError will be raised. + + Note, the code is outputted by changing the background color. Then + two spaces are written to the terminal. Finally, the terminal is + reset back to how it was. + """ + buf = io.StringIO() + + def draw_border(): + for i in range(quiet_zone): + buf.write(background) + + if module_color in tables.term_colors: + data = '\033[{0}m \033[0m'.format( + tables.term_colors[module_color]) + elif 0 <= module_color <= 256: + data = '\033[48;5;{0}m \033[0m'.format(module_color) + else: + raise ValueError('The module color, {0}, must a key in ' + 'pyqrcode.tables.term_colors or a number ' + 'between 0 and 256.'.format( + module_color)) + + if background in tables.term_colors: + background = '\033[{0}m \033[0m'.format( + tables.term_colors[background]) + elif 0 <= background <= 256: + background = '\033[48;5;{0}m \033[0m'.format(background) + else: + raise ValueError('The background color, {0}, must a key in ' + 'pyqrcode.tables.term_colors or a number ' + 'between 0 and 256.'.format( + background)) + + #This will be the beginning and ending row for the code. + border_row = background * (len(code[0]) + (2 * quiet_zone)) + + #Make sure we begin on a new line, and force the terminal back + #to normal + buf.write('\n') + + #QRCodes have a quiet zone consisting of background modules + for i in range(quiet_zone): + buf.write(border_row) + buf.write('\n') + + for row in code: + #Each code has a quiet zone on the left side, this is the left + #border for this code + draw_border() + + for bit in row: + if bit == 1: + buf.write(data) + elif bit == 0: + buf.write(background) + + #Each row ends with a quiet zone on the right side, this is the + #right hand border background modules + draw_border() + buf.write('\n') + + #QRCodes have a background quiet zone row following the code + for i in range(quiet_zone): + buf.write(border_row) + buf.write('\n') + + return buf.getvalue() + +def _text(code, quiet_zone=4): + """This method returns a text based representation of the QR code. + This is useful for debugging purposes. + """ + buf = io.StringIO() + + border_row = '0' * (len(code[0]) + (quiet_zone*2)) + + #Every QR code start with a quiet zone at the top + for b in range(quiet_zone): + buf.write(border_row) + buf.write('\n') + + for row in code: + #Draw the starting quiet zone + for b in range(quiet_zone): + buf.write('0') + + #Actually draw the QR code + for bit in row: + if bit == 1: + buf.write('1') + elif bit == 0: + buf.write('0') + #This is for debugging unfinished QR codes, + #unset pixels will be spaces. + else: + buf.write(' ') + + #Draw the ending quiet zone + for b in range(quiet_zone): + buf.write('0') + buf.write('\n') + + #Every QR code ends with a quiet zone at the bottom + for b in range(quiet_zone): + buf.write(border_row) + buf.write('\n') + + return buf.getvalue() + +def _xbm(code, scale=1, quiet_zone=4): + """This function will format the QR code as a X BitMap. + This can be used to display the QR code with Tkinter. + """ + try: + str = unicode # Python 2 + except NameError: + str = __builtins__['str'] + + buf = io.StringIO() + + # Calculate the width in pixels + pixel_width = (len(code[0]) + quiet_zone * 2) * scale + + # Add the size information and open the pixel data section + buf.write('#define im_width ') + buf.write(str(pixel_width)) + buf.write('\n') + buf.write('#define im_height ') + buf.write(str(pixel_width)) + buf.write('\n') + buf.write('static char im_bits[] = {\n') + + # Calculate the number of bytes per row + byte_width = int(math.ceil(pixel_width / 8.0)) + + # Add the top quiet zone + buf.write(('0x00,' * byte_width + '\n') * quiet_zone * scale) + for row in code: + # Add the left quiet zone + row_bits = '0' * quiet_zone * scale + # Add the actual QR code + for pixel in row: + row_bits += str(pixel) * scale + # Add the right quiet zone + row_bits += '0' * quiet_zone * scale + # Format the row + formated_row = '' + for b in range(byte_width): + formated_row += '0x{0:02x},'.format(int(row_bits[:8][::-1], 2)) + row_bits = row_bits[8:] + formated_row += '\n' + # Add the formatted row + buf.write(formated_row * scale) + # Add the bottom quiet zone and close the pixel data section + buf.write(('0x00,' * byte_width + '\n') * quiet_zone * scale) + buf.write('};') + + return buf.getvalue() + +def _svg(code, version, file, scale=1, module_color='#000', background=None, + quiet_zone=4, xmldecl=True, svgns=True, title=None, svgclass='pyqrcode', + lineclass='pyqrline', omithw=False, debug=False): + """This function writes the QR code out as an SVG document. The + code is drawn by drawing only the modules corresponding to a 1. They + are drawn using a line, such that contiguous modules in a row + are drawn with a single line. The file parameter is used to + specify where to write the document to. It can either be a writable (binary) + stream or a file path. The scale parameter is sets how large to draw + a single module. By default one pixel is used to draw a single + module. This may make the code to small to be read efficiently. + Increasing the scale will make the code larger. This method will accept + fractional scales (e.g. 2.5). + + :param module_color: Color of the QR code (default: ``#000`` (black)) + :param background: Optional background color. + (default: ``None`` (no background)) + :param quiet_zone: Border around the QR code (also known as quiet zone) + (default: ``4``). Set to zero (``0``) if the code shouldn't + have a border. + :param xmldecl: Inidcates if the XML declaration header should be written + (default: ``True``) + :param svgns: Indicates if the SVG namespace should be written + (default: ``True``) + :param title: Optional title of the generated SVG document. + :param svgclass: The CSS class of the SVG document + (if set to ``None``, the SVG element won't have a class). + :param lineclass: The CSS class of the path element + (if set to ``None``, the path won't have a class). + :param omithw: Indicates if width and height attributes should be + omitted (default: ``False``). If these attributes are omitted, + a ``viewBox`` attribute will be added to the document. + :param debug: Inidicates if errors in the QR code should be added to the + output (default: ``False``). + """ + from functools import partial + from xml.sax.saxutils import quoteattr + + def write_unicode(write_meth, unicode_str): + """\ + Encodes the provided string into UTF-8 and writes the result using + the `write_meth`. + """ + write_meth(unicode_str.encode('utf-8')) + + def line(x, y, length, relative): + """Returns coordinates to draw a line with the provided length. + """ + return '{0}{1} {2}h{3}'.format(('m' if relative else 'M'), x, y, length) + + def errline(col_number, row_number): + """Returns the coordinates to draw an error bit. + """ + # Debug path uses always absolute coordinates + # .5 == stroke / 2 + return line(col_number + quiet_zone, row_number + quiet_zone + .5, 1, False) + + f, autoclose = _get_writable(file, 'wb') + write = partial(write_unicode, f.write) + write_bytes = f.write + # Write the document header + if xmldecl: + write_bytes(b'\n') + write_bytes(b'') + if title is not None: + write('{0}'.format(title)) + + # Draw a background rectangle if necessary + if background is not None: + write('' + .format(size, background)) + write_bytes(b'') + if debug and debug_path: + write_bytes(b''.format(debug_path)) + # Close document + write_bytes(b'\n') + if autoclose: + f.close() + + +def _png(code, version, file, scale=1, module_color=(0, 0, 0, 255), + background=(255, 255, 255, 255), quiet_zone=4, debug=False): + """See: pyqrcode.QRCode.png() + + This function was abstracted away from QRCode to allow for the output of + QR codes during the build process, i.e. for debugging. It works + just the same except you must specify the code's version. This is needed + to calculate the PNG's size. + + This method will write the given file out as a PNG file. Note, it + depends on the PyPNG module to do this. + + :param module_color: Color of the QR code (default: ``(0, 0, 0, 255)`` (black)) + :param background: Optional background color. If set to ``None`` the PNG + will have a transparent background. + (default: ``(255, 255, 255, 255)`` (white)) + :param quiet_zone: Border around the QR code (also known as quiet zone) + (default: ``4``). Set to zero (``0``) if the code shouldn't + have a border. + :param debug: Inidicates if errors in the QR code should be added (as red + modules) to the output (default: ``False``). + """ + import png + + # Coerce scale parameter into an integer + try: + scale = int(scale) + except ValueError: + raise ValueError('The scale parameter must be an integer') + + def scale_code(size): + """To perform the scaling we need to inflate the number of bits. + The PNG library expects all of the bits when it draws the PNG. + Effectively, we double, tripple, etc. the number of columns and + the number of rows. + """ + # This is one row's worth of each possible module + # PNG's use 0 for black and 1 for white, this is the + # reverse of the QR standard + black = [0] * scale + white = [1] * scale + + # Tuple to lookup colors + # The 3rd color is the module_color unless "debug" is enabled + colors = (white, black, (([2] * scale) if debug else black)) + + # Whitespace added on the left and right side + border_module = white * quiet_zone + # This is the row to show up at the top and bottom border + border_row = [[1] * size] * scale * quiet_zone + + # This will hold the final PNG's bits + bits = [] + + # Add scale rows before the code as a border, + # as per the standard + bits.extend(border_row) + + # Add each row of the to the final PNG bits + for row in code: + tmp_row = [] + + # Add one all white module to the beginning + # to create the vertical border + tmp_row.extend(border_module) + + # Go through each bit in the code + for bit in row: + # Use the standard color or the "debug" color + tmp_row.extend(colors[(bit if bit in (0, 1) else 2)]) + + # Add one all white module to the end + # to create the vertical border + tmp_row.extend(border_module) + + # Copy each row scale times + for n in range(scale): + bits.append(tmp_row) + + # Add the bottom border + bits.extend(border_row) + + return bits + + def png_pallete_color(color): + """This creates a palette color from a list or tuple. The list or + tuple must be of length 3 (for rgb) or 4 (for rgba). The values + must be between 0 and 255. Note rgb colors will be given an added + alpha component set to 255. + + The pallete color is represented as a list, this is what is returned. + """ + if color is None: + return () + if not isinstance(color, (tuple, list)): + r, g, b = _hex_to_rgb(color) + return r, g, b, 255 + rgba = [] + if not (3 <= len(color) <= 4): + raise ValueError('Colors must be a list or tuple of length ' + ' 3 or 4. You passed in "{0}".'.format(color)) + for c in color: + c = int(c) + if 0 <= c <= 255: + rgba.append(int(c)) + else: + raise ValueError('Color components must be between 0 and 255') + # Make all colors have an alpha channel + if len(rgba) == 3: + rgba.append(255) + return tuple(rgba) + + if module_color is None: + raise ValueError('The module_color must not be None') + + bitdepth = 1 + # foreground aka module color + fg_col = png_pallete_color(module_color) + transparent = background is None + # If background color is set to None, the inverse color of the + # foreground color is calculated + bg_col = png_pallete_color(background) if background is not None else tuple([255 - c for c in fg_col]) + # Assume greyscale if module color is black and background color is white + greyscale = fg_col[:3] == (0, 0, 0) and (not debug and transparent or bg_col == (255, 255, 255, 255)) + transparent_color = 1 if transparent and greyscale else None + palette = [fg_col, bg_col] if not greyscale else None + if debug: + # Add "red" as color for error modules + palette.append((255, 0, 0, 255)) + bitdepth = 2 + + # The size of the PNG + size = _get_png_size(version, scale, quiet_zone) + + # We need to increase the size of the code to match up to the + # scale parameter. + code_rows = scale_code(size) + + # Write out the PNG + f, autoclose = _get_writable(file, 'wb') + w = png.Writer(width=size, height=size, greyscale=greyscale, + transparent=transparent_color, palette=palette, + bitdepth=bitdepth) + try: + w.write(f, code_rows) + finally: + if autoclose: + f.close() + + +def _eps(code, version, file_or_path, scale=1, module_color=(0, 0, 0), + background=None, quiet_zone=4): + """This function writes the QR code out as an EPS document. The + code is drawn by drawing only the modules corresponding to a 1. They + are drawn using a line, such that contiguous modules in a row + are drawn with a single line. The file parameter is used to + specify where to write the document to. It can either be a writable (text) + stream or a file path. The scale parameter is sets how large to draw + a single module. By default one point (1/72 inch) is used to draw a single + module. This may make the code to small to be read efficiently. + Increasing the scale will make the code larger. This function will accept + fractional scales (e.g. 2.5). + + :param module_color: Color of the QR code (default: ``(0, 0, 0)`` (black)) + The color can be specified as triple of floats (range: 0 .. 1) or + triple of integers (range: 0 .. 255) or as hexadecimal value (i.e. + ``#36c`` or ``#33B200``). + :param background: Optional background color. + (default: ``None`` (no background)). See `module_color` for the + supported values. + :param quiet_zone: Border around the QR code (also known as quiet zone) + (default: ``4``). Set to zero (``0``) if the code shouldn't + have a border. + """ + from functools import partial + import time + import textwrap + + def write_line(writemeth, content): + """\ + Writes `content` and ``LF``. + """ + # Postscript: Max. 255 characters per line + for line in textwrap.wrap(content, 255): + writemeth(line) + writemeth('\n') + + def line(offset, length): + """\ + Returns coordinates to draw a line with the provided length. + """ + res = '' + if offset > 0: + res = ' {0} 0 m'.format(offset) + res += ' {0} 0 l'.format(length) + return res + + def rgb_to_floats(color): + """\ + Converts the provided color into an acceptable format for Postscript's + ``setrgbcolor`` + """ + def to_float(clr): + if isinstance(clr, float): + if not 0.0 <= clr <= 1.0: + raise ValueError('Invalid color "{0}". Not in range 0 .. 1' + .format(clr)) + return clr + if not 0 <= clr <= 255: + raise ValueError('Invalid color "{0}". Not in range 0 .. 255' + .format(clr)) + return 1/255.0 * clr if clr != 1 else clr + + if not isinstance(color, (tuple, list)): + color = _hex_to_rgb(color) + return tuple([to_float(i) for i in color]) + + f, autoclose = _get_writable(file_or_path, 'w') + writeline = partial(write_line, f.write) + size = tables.version_size[version] * scale + (2 * quiet_zone * scale) + # Write common header + writeline('%!PS-Adobe-3.0 EPSF-3.0') + writeline('%%Creator: PyQRCode ') + writeline('%%CreationDate: {0}'.format(time.strftime("%Y-%m-%d %H:%M:%S"))) + writeline('%%DocumentData: Clean7Bit') + writeline('%%BoundingBox: 0 0 {0} {0}'.format(size)) + # Write the shortcuts + writeline('/M { moveto } bind def') + writeline('/m { rmoveto } bind def') + writeline('/l { rlineto } bind def') + mod_color = module_color if module_color == (0, 0, 0) else rgb_to_floats(module_color) + if background is not None: + writeline('{0:f} {1:f} {2:f} setrgbcolor clippath fill' + .format(*rgb_to_floats(background))) + if mod_color == (0, 0, 0): + # Reset RGB color back to black iff module color is black + # In case module color != black set the module RGB color later + writeline('0 0 0 setrgbcolor') + if mod_color != (0, 0, 0): + writeline('{0:f} {1:f} {2:f} setrgbcolor'.format(*mod_color)) + if scale != 1: + writeline('{0} {0} scale'.format(scale)) + writeline('newpath') + # Current pen position y-axis + # Note: 0, 0 = lower left corner in PS coordinate system + y = tables.version_size[version] + quiet_zone + .5 # .5 = linewidth / 2 + last_bit = 1 + # Loop through each row of the code + for row in code: + offset = 0 # Set x-offset of the pen + length = 0 + y -= 1 # Move pen along y-axis + coord = '{0} {1} M'.format(quiet_zone, y) # Move pen to initial pos + for bit in row: + if bit != last_bit: + if length: + coord += line(offset, length) + offset = 0 + length = 0 + last_bit = bit + if bit == 1: + length += 1 + else: + offset += 1 + if length: + coord += line(offset, length) + writeline(coord) + writeline('stroke') + writeline('%%EOF') + if autoclose: + f.close() + + +def _hex_to_rgb(color): + """\ + Helper function to convert a color provided in hexadecimal format + as RGB triple. + """ + if color[0] == '#': + color = color[1:] + if len(color) == 3: + color = color[0] * 2 + color[1] * 2 + color[2] * 2 + if len(color) != 6: + raise ValueError('Input #{0} is not in #RRGGBB format'.format(color)) + return [int(n, 16) for n in (color[:2], color[2:4], color[4:])] diff --git a/pyqrcode/tables.py b/pyqrcode/tables.py new file mode 100644 index 0000000..dbd2893 --- /dev/null +++ b/pyqrcode/tables.py @@ -0,0 +1,794 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2013, Michael Nooner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +"""This module lists out all of the tables needed to create a QR code. +If you are viewing this in the HTML documentation, I recommend reading the +actual file instead. The formating for the tables is much more readable. +""" +from __future__ import division, unicode_literals + +#: This defines the QR Code's 'mode' which sets what +#: type of code it is along with its size. +modes = { + 'numeric': 1, + 'alphanumeric': 2, + 'binary': 4, + 'kanji': 8, +} + +#: This defines the amount of error correction. The dictionary +#: allows the user to specify this in several ways. +error_level = {'L': 'L', 'l': 'L', '7%': 'L', .7: 'L', + 'M': 'M', 'm': 'M', '15%': 'M', .15: 'M', + 'Q': 'Q', 'q': 'Q', '25%': 'Q', .25: 'Q', + 'H': 'H', 'h': 'H', '30%': 'H', .30: 'H'} + +#: This is a dictionary holds how long the "data length" field is for +#: each version and mode of the QR Code. +data_length_field = {9: {1: 10, 2: 9, 4: 8, 8: 8}, + 26: {1: 12, 2: 11, 4: 16, 8: 10}, + 40: {1: 14, 2: 13, 4: 16, 8: 12}} + +#: QR Codes uses a unique ASCII-like table for the 'alphanumeric' mode. +#: This is a dictionary representing that unique table, where the +#: keys are the possible characters in the data and the values +#: are the character's numeric representation. +ascii_codes = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, + '8': 8, '9': 9, 'A': 10, 'B': 11, 'C': 12, 'D': 13, 'E': 14, + 'F': 15, 'G': 16, 'H': 17, 'I': 18, 'J': 19, 'K': 20, 'L': 21, + 'M': 22, 'N': 23, 'O': 24, 'P': 25, 'Q': 26, 'R': 27, 'S': 28, + 'T': 29, 'U': 30, 'V': 31, 'W': 32, 'X': 33, 'Y': 34, 'Z': 35, + ' ': 36, '$': 37, '%': 38, '*': 39, '+': 40, '-': 41, '.': 42, + '/': 43, ':': 44} + +#: This array specifies the size of a QR Code in pixels. These numbers are +#: defined in the standard. The indexes correspond to the QR Code's +#: version number. This array was taken from: +#: +#: http://www.denso-wave.com/qrcode/vertable1-e.html +version_size = [None, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, + 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, + 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, + 141, 145, 149, 153, 157, 161, 165, 169, 173, 177] + +#: This dictionary lists the data capacity for all possible QR Codes. +#: This dictionary is organized where the first key corresponds to the +#: QR Code version number. The next key corresponds to the error +#: correction level, see error. The final key corresponds to +#: the mode number, see modes. The zero mode number represents the +#: possible "data bits." This table was taken from: +#: +#: http://www.denso-wave.com/qrcode/vertable1-e.html +data_capacity = { + 1: { + "L": {0: 152, 1: 41, 2: 25, 4: 17, 8: 10, }, + "M": {0: 128, 1: 34, 2: 20, 4: 14, 8: 8, }, + "Q": {0: 104, 1: 27, 2: 16, 4: 11, 8: 7, }, + "H": {0: 72, 1: 17, 2: 10, 4: 7, 8: 4, }}, + 2: { + "L": {0: 272, 1: 77, 2: 47, 4: 32, 8: 20, }, + "M": {0: 224, 1: 63, 2: 38, 4: 26, 8: 16, }, + "Q": {0: 176, 1: 48, 2: 29, 4: 20, 8: 12, }, + "H": {0: 128, 1: 34, 2: 20, 4: 14, 8: 8, }}, + 3: { + "L": {0: 440, 1: 127, 2: 77, 4: 53, 8: 32, }, + "M": {0: 352, 1: 101, 2: 61, 4: 42, 8: 26, }, + "Q": {0: 272, 1: 77, 2: 47, 4: 32, 8: 20, }, + "H": {0: 208, 1: 58, 2: 35, 4: 24, 8: 15, }}, + 4: { + "L": {0: 640, 1: 187, 2: 114, 4: 78, 8: 48, }, + "M": {0: 512, 1: 149, 2: 90, 4: 62, 8: 38, }, + "Q": {0: 384, 1: 111, 2: 67, 4: 46, 8: 28, }, + "H": {0: 288, 1: 82, 2: 50, 4: 34, 8: 21, }}, + 5: { + "L": {0: 864, 1: 255, 2: 154, 4: 106, 8: 65, }, + "M": {0: 688, 1: 202, 2: 122, 4: 84, 8: 52, }, + "Q": {0: 496, 1: 144, 2: 87, 4: 60, 8: 37, }, + "H": {0: 368, 1: 106, 2: 64, 4: 44, 8: 27, }}, + 6: { + "L": {0: 1088, 1: 322, 2: 195, 4: 134, 8: 82, }, + "M": {0: 864, 1: 255, 2: 154, 4: 106, 8: 65, }, + "Q": {0: 608, 1: 178, 2: 108, 4: 74, 8: 45, }, + "H": {0: 480, 1: 139, 2: 84, 4: 58, 8: 36, }}, + 7: { + "L": {0: 1248, 1: 370, 2: 224, 4: 154, 8: 95, }, + "M": {0: 992, 1: 293, 2: 178, 4: 122, 8: 75, }, + "Q": {0: 704, 1: 207, 2: 125, 4: 86, 8: 53, }, + "H": {0: 528, 1: 154, 2: 93, 4: 64, 8: 39, }}, + 8: { + "L": {0: 1552, 1: 461, 2: 279, 4: 192, 8: 118, }, + "M": {0: 1232, 1: 365, 2: 221, 4: 152, 8: 93, }, + "Q": {0: 880, 1: 259, 2: 157, 4: 108, 8: 66, }, + "H": {0: 688, 1: 202, 2: 122, 4: 84, 8: 52, }}, + 9: { + "L": {0: 1856, 1: 552, 2: 335, 4: 230, 8: 141, }, + "M": {0: 1456, 1: 432, 2: 262, 4: 180, 8: 111, }, + "Q": {0: 1056, 1: 312, 2: 189, 4: 130, 8: 80, }, + "H": {0: 800, 1: 235, 2: 143, 4: 98, 8: 60, }}, + 10: { + "L": {0: 2192, 1: 652, 2: 395, 4: 271, 8: 167, }, + "M": {0: 1728, 1: 513, 2: 311, 4: 213, 8: 131, }, + "Q": {0: 1232, 1: 364, 2: 221, 4: 151, 8: 93, }, + "H": {0: 976, 1: 288, 2: 174, 4: 119, 8: 74, }}, + 11: { + "L": {0: 2592, 1: 772, 2: 468, 4: 321, 8: 198, }, + "M": {0: 2032, 1: 604, 2: 366, 4: 251, 8: 155, }, + "Q": {0: 1440, 1: 427, 2: 259, 4: 177, 8: 109, }, + "H": {0: 1120, 1: 331, 2: 200, 4: 137, 8: 85, }}, + 12: { + "L": {0: 2960, 1: 883, 2: 535, 4: 367, 8: 226, }, + "M": {0: 2320, 1: 691, 2: 419, 4: 287, 8: 177, }, + "Q": {0: 1648, 1: 489, 2: 296, 4: 203, 8: 125, }, + "H": {0: 1264, 1: 374, 2: 227, 4: 155, 8: 96, }}, + 13: { + "L": {0: 3424, 1: 1022, 2: 619, 4: 425, 8: 262, }, + "M": {0: 2672, 1: 796, 2: 483, 4: 331, 8: 204, }, + "Q": {0: 1952, 1: 580, 2: 352, 4: 241, 8: 149, }, + "H": {0: 1440, 1: 427, 2: 259, 4: 177, 8: 109, }}, + 14: { + "L": {0: 3688, 1: 1101, 2: 667, 4: 458, 8: 282, }, + "M": {0: 2920, 1: 871, 2: 528, 4: 362, 8: 223, }, + "Q": {0: 2088, 1: 621, 2: 376, 4: 258, 8: 159, }, + "H": {0: 1576, 1: 468, 2: 283, 4: 194, 8: 120, }}, + 15: { + "L": {0: 4184, 1: 1250, 2: 758, 4: 520, 8: 320, }, + "M": {0: 3320, 1: 991, 2: 600, 4: 412, 8: 254, }, + "Q": {0: 2360, 1: 703, 2: 426, 4: 292, 8: 180, }, + "H": {0: 1784, 1: 530, 2: 321, 4: 220, 8: 136, }}, + 16: { + "L": {0: 4712, 1: 1408, 2: 854, 4: 586, 8: 361, }, + "M": {0: 3624, 1: 1082, 2: 656, 4: 450, 8: 277, }, + "Q": {0: 2600, 1: 775, 2: 470, 4: 322, 8: 198, }, + "H": {0: 2024, 1: 602, 2: 365, 4: 250, 8: 154, }}, + 17: { + "L": {0: 5176, 1: 1548, 2: 938, 4: 644, 8: 397, }, + "M": {0: 4056, 1: 1212, 2: 734, 4: 504, 8: 310, }, + "Q": {0: 2936, 1: 876, 2: 531, 4: 364, 8: 224, }, + "H": {0: 2264, 1: 674, 2: 408, 4: 280, 8: 173, }}, + 18: { + "L": {0: 5768, 1: 1725, 2: 1046, 4: 718, 8: 442, }, + "M": {0: 4504, 1: 1346, 2: 816, 4: 560, 8: 345, }, + "Q": {0: 3176, 1: 948, 2: 574, 4: 394, 8: 243, }, + "H": {0: 2504, 1: 746, 2: 452, 4: 310, 8: 191, }}, + 19: { + "L": {0: 6360, 1: 1903, 2: 1153, 4: 792, 8: 488, }, + "M": {0: 5016, 1: 1500, 2: 909, 4: 624, 8: 384, }, + "Q": {0: 3560, 1: 1063, 2: 644, 4: 442, 8: 272, }, + "H": {0: 2728, 1: 813, 2: 493, 4: 338, 8: 208, }}, + 20: { + "L": {0: 6888, 1: 2061, 2: 1249, 4: 858, 8: 528, }, + "M": {0: 5352, 1: 1600, 2: 970, 4: 666, 8: 410, }, + "Q": {0: 3880, 1: 1159, 2: 702, 4: 482, 8: 297, }, + "H": {0: 3080, 1: 919, 2: 557, 4: 382, 8: 235, }}, + 21: { + "L": {0: 7456, 1: 2232, 2: 1352, 4: 929, 8: 572, }, + "M": {0: 5712, 1: 1708, 2: 1035, 4: 711, 8: 438, }, + "Q": {0: 4096, 1: 1224, 2: 742, 4: 509, 8: 314, }, + "H": {0: 3248, 1: 969, 2: 587, 4: 403, 8: 248, }}, + 22: { + "L": {0: 8048, 1: 2409, 2: 1460, 4: 1003, 8: 618, }, + "M": {0: 6256, 1: 1872, 2: 1134, 4: 779, 8: 480, }, + "Q": {0: 4544, 1: 1358, 2: 823, 4: 565, 8: 348, }, + "H": {0: 3536, 1: 1056, 2: 640, 4: 439, 8: 270, }}, + 23: { + "L": {0: 8752, 1: 2620, 2: 1588, 4: 1091, 8: 672, }, + "M": {0: 6880, 1: 2059, 2: 1248, 4: 857, 8: 528, }, + "Q": {0: 4912, 1: 1468, 2: 890, 4: 611, 8: 376, }, + "H": {0: 3712, 1: 1108, 2: 672, 4: 461, 8: 284, }}, + 24: { + "L": {0: 9392, 1: 2812, 2: 1704, 4: 1171, 8: 721, }, + "M": {0: 7312, 1: 2188, 2: 1326, 4: 911, 8: 561, }, + "Q": {0: 5312, 1: 1588, 2: 963, 4: 661, 8: 407, }, + "H": {0: 4112, 1: 1228, 2: 744, 4: 511, 8: 315, }}, + 25: { + "L": {0: 10208, 1: 3057, 2: 1853, 4: 1273, 8: 784, }, + "M": {0: 8000, 1: 2395, 2: 1451, 4: 997, 8: 614, }, + "Q": {0: 5744, 1: 1718, 2: 1041, 4: 715, 8: 440, }, + "H": {0: 4304, 1: 1286, 2: 779, 4: 535, 8: 330, }}, + 26: { + "L": {0: 10960, 1: 3283, 2: 1990, 4: 1367, 8: 842, }, + "M": {0: 8496, 1: 2544, 2: 1542, 4: 1059, 8: 652, }, + "Q": {0: 6032, 1: 1804, 2: 1094, 4: 751, 8: 462, }, + "H": {0: 4768, 1: 1425, 2: 864, 4: 593, 8: 365, }}, + 27: { + "L": {0: 11744, 1: 3514, 2: 2132, 4: 1465, 8: 902, }, + "M": {0: 9024, 1: 2701, 2: 1637, 4: 1125, 8: 692, }, + "Q": {0: 6464, 1: 1933, 2: 1172, 4: 805, 8: 496, }, + "H": {0: 5024, 1: 1501, 2: 910, 4: 625, 8: 385, }}, + 28: { + "L": {0: 12248, 1: 3669, 2: 2223, 4: 1528, 8: 940, }, + "M": {0: 9544, 1: 2857, 2: 1732, 4: 1190, 8: 732, }, + "Q": {0: 6968, 1: 2085, 2: 1263, 4: 868, 8: 534, }, + "H": {0: 5288, 1: 1581, 2: 958, 4: 658, 8: 405, }}, + 29: { + "L": {0: 13048, 1: 3909, 2: 2369, 4: 1628, 8: 1002, }, + "M": {0: 10136, 1: 3035, 2: 1839, 4: 1264, 8: 778, }, + "Q": {0: 7288, 1: 2181, 2: 1322, 4: 908, 8: 559, }, + "H": {0: 5608, 1: 1677, 2: 1016, 4: 698, 8: 430, }}, + 30: { + "L": {0: 13880, 1: 4158, 2: 2520, 4: 1732, 8: 1066, }, + "M": {0: 10984, 1: 3289, 2: 1994, 4: 1370, 8: 843, }, + "Q": {0: 7880, 1: 2358, 2: 1429, 4: 982, 8: 604, }, + "H": {0: 5960, 1: 1782, 2: 1080, 4: 742, 8: 457, }}, + 31: { + "L": {0: 14744, 1: 4417, 2: 2677, 4: 1840, 8: 1132, }, + "M": {0: 11640, 1: 3486, 2: 2113, 4: 1452, 8: 894, }, + "Q": {0: 8264, 1: 2473, 2: 1499, 4: 1030, 8: 634, }, + "H": {0: 6344, 1: 1897, 2: 1150, 4: 790, 8: 486, }}, + 32: { + "L": {0: 15640, 1: 4686, 2: 2840, 4: 1952, 8: 1201, }, + "M": {0: 12328, 1: 3693, 2: 2238, 4: 1538, 8: 947, }, + "Q": {0: 8920, 1: 2670, 2: 1618, 4: 1112, 8: 684, }, + "H": {0: 6760, 1: 2022, 2: 1226, 4: 842, 8: 518, }}, + 33: { + "L": {0: 16568, 1: 4965, 2: 3009, 4: 2068, 8: 1273, }, + "M": {0: 13048, 1: 3909, 2: 2369, 4: 1628, 8: 1002, }, + "Q": {0: 9368, 1: 2805, 2: 1700, 4: 1168, 8: 719, }, + "H": {0: 7208, 1: 2157, 2: 1307, 4: 898, 8: 553, }}, + 34: { + "L": {0: 17528, 1: 5253, 2: 3183, 4: 2188, 8: 1347, }, + "M": {0: 13800, 1: 4134, 2: 2506, 4: 1722, 8: 1060, }, + "Q": {0: 9848, 1: 2949, 2: 1787, 4: 1228, 8: 756, }, + "H": {0: 7688, 1: 2301, 2: 1394, 4: 958, 8: 590, }}, + 35: { + "L": {0: 18448, 1: 5529, 2: 3351, 4: 2303, 8: 1417, }, + "M": {0: 14496, 1: 4343, 2: 2632, 4: 1809, 8: 1113, }, + "Q": {0: 10288, 1: 3081, 2: 1867, 4: 1283, 8: 790, }, + "H": {0: 7888, 1: 2361, 2: 1431, 4: 983, 8: 605, }}, + 36: { + "L": {0: 19472, 1: 5836, 2: 3537, 4: 2431, 8: 1496, }, + "M": {0: 15312, 1: 4588, 2: 2780, 4: 1911, 8: 1176, }, + "Q": {0: 10832, 1: 3244, 2: 1966, 4: 1351, 8: 832, }, + "H": {0: 8432, 1: 2524, 2: 1530, 4: 1051, 8: 647, }}, + 37: { + "L": {0: 20528, 1: 6153, 2: 3729, 4: 2563, 8: 1577, }, + "M": {0: 15936, 1: 4775, 2: 2894, 4: 1989, 8: 1224, }, + "Q": {0: 11408, 1: 3417, 2: 2071, 4: 1423, 8: 876, }, + "H": {0: 8768, 1: 2625, 2: 1591, 4: 1093, 8: 673, }}, + 38: { + "L": {0: 21616, 1: 6479, 2: 3927, 4: 2699, 8: 1661, }, + "M": {0: 16816, 1: 5039, 2: 3054, 4: 2099, 8: 1292, }, + "Q": {0: 12016, 1: 3599, 2: 2181, 4: 1499, 8: 923, }, + "H": {0: 9136, 1: 2735, 2: 1658, 4: 1139, 8: 701, }}, + 39: { + "L": {0: 22496, 1: 6743, 2: 4087, 4: 2809, 8: 1729, }, + "M": {0: 17728, 1: 5313, 2: 3220, 4: 2213, 8: 1362, }, + "Q": {0: 12656, 1: 3791, 2: 2298, 4: 1579, 8: 972, }, + "H": {0: 9776, 1: 2927, 2: 1774, 4: 1219, 8: 750, }}, + 40: { + "L": {0: 23648, 1: 7089, 2: 4296, 4: 2953, 8: 1817, }, + "M": {0: 18672, 1: 5596, 2: 3391, 4: 2331, 8: 1435, }, + "Q": {0: 13328, 1: 3993, 2: 2420, 4: 1663, 8: 1024, }, + "H": {0: 10208, 1: 3057, 2: 1852, 4: 1273, 8: 784, }} +} + +#: This table defines the "Error Correction Code Words and Block Information." +#: The table lists the number of error correction words that are required +#: to be generated for each version and error correction level. The table +#: is accessed by first using the version number as a key and then the +#: error level. The array values correspond to these columns from the source +#: table: +#: +#: +----------------------------+ +#: |0 | EC Code Words Per Block | +#: +----------------------------+ +#: |1 | Block 1 Count | +#: +----------------------------+ +#: |2 | Block 1 Data Code Words | +#: +----------------------------+ +#: |3 | Block 2 Count | +#: +----------------------------+ +#: |4 | Block 2 Data Code Words | +#: +----------------------------+ +#: +#: This table was taken from: +#: +#: http://www.thonky.com/qr-code-tutorial/error-correction-table/ +eccwbi = { + 1: { + 'L': [7, 1, 19, 0, 0, ], + 'M': [10, 1, 16, 0, 0, ], + 'Q': [13, 1, 13, 0, 0, ], + 'H': [17, 1, 9, 0, 0, ], + }, + 2: { + 'L': [10, 1, 34, 0, 0, ], + 'M': [16, 1, 28, 0, 0, ], + 'Q': [22, 1, 22, 0, 0, ], + 'H': [28, 1, 16, 0, 0, ], + }, + 3: { + 'L': [15, 1, 55, 0, 0, ], + 'M': [26, 1, 44, 0, 0, ], + 'Q': [18, 2, 17, 0, 0, ], + 'H': [22, 2, 13, 0, 0, ], + }, + 4: { + 'L': [20, 1, 80, 0, 0, ], + 'M': [18, 2, 32, 0, 0, ], + 'Q': [26, 2, 24, 0, 0, ], + 'H': [16, 4, 9, 0, 0, ], + }, + 5: { + 'L': [26, 1, 108, 0, 0, ], + 'M': [24, 2, 43, 0, 0, ], + 'Q': [18, 2, 15, 2, 16, ], + 'H': [22, 2, 11, 2, 12, ], + }, + 6: { + 'L': [18, 2, 68, 0, 0, ], + 'M': [16, 4, 27, 0, 0, ], + 'Q': [24, 4, 19, 0, 0, ], + 'H': [28, 4, 15, 0, 0, ], + }, + 7: { + 'L': [20, 2, 78, 0, 0, ], + 'M': [18, 4, 31, 0, 0, ], + 'Q': [18, 2, 14, 4, 15, ], + 'H': [26, 4, 13, 1, 14, ], + }, + 8: { + 'L': [24, 2, 97, 0, 0, ], + 'M': [22, 2, 38, 2, 39, ], + 'Q': [22, 4, 18, 2, 19, ], + 'H': [26, 4, 14, 2, 15, ], + }, + 9: { + 'L': [30, 2, 116, 0, 0, ], + 'M': [22, 3, 36, 2, 37, ], + 'Q': [20, 4, 16, 4, 17, ], + 'H': [24, 4, 12, 4, 13, ], + }, + 10: { + 'L': [18, 2, 68, 2, 69, ], + 'M': [26, 4, 43, 1, 44, ], + 'Q': [24, 6, 19, 2, 20, ], + 'H': [28, 6, 15, 2, 16, ], + }, + 11: { + 'L': [20, 4, 81, 0, 0, ], + 'M': [30, 1, 50, 4, 51, ], + 'Q': [28, 4, 22, 4, 23, ], + 'H': [24, 3, 12, 8, 13, ], + }, + 12: { + 'L': [24, 2, 92, 2, 93, ], + 'M': [22, 6, 36, 2, 37, ], + 'Q': [26, 4, 20, 6, 21, ], + 'H': [28, 7, 14, 4, 15, ], + }, + 13: { + 'L': [26, 4, 107, 0, 0, ], + 'M': [22, 8, 37, 1, 38, ], + 'Q': [24, 8, 20, 4, 21, ], + 'H': [22, 12, 11, 4, 12, ], + }, + 14: { + 'L': [30, 3, 115, 1, 116, ], + 'M': [24, 4, 40, 5, 41, ], + 'Q': [20, 11, 16, 5, 17, ], + 'H': [24, 11, 12, 5, 13, ], + }, + 15: { + 'L': [22, 5, 87, 1, 88, ], + 'M': [24, 5, 41, 5, 42, ], + 'Q': [30, 5, 24, 7, 25, ], + 'H': [24, 11, 12, 7, 13, ], + }, + 16: { + 'L': [24, 5, 98, 1, 99, ], + 'M': [28, 7, 45, 3, 46, ], + 'Q': [24, 15, 19, 2, 20, ], + 'H': [30, 3, 15, 13, 16, ], + }, + 17: { + 'L': [28, 1, 107, 5, 108, ], + 'M': [28, 10, 46, 1, 47, ], + 'Q': [28, 1, 22, 15, 23, ], + 'H': [28, 2, 14, 17, 15, ], + }, + 18: { + 'L': [30, 5, 120, 1, 121, ], + 'M': [26, 9, 43, 4, 44, ], + 'Q': [28, 17, 22, 1, 23, ], + 'H': [28, 2, 14, 19, 15, ], + }, + 19: { + 'L': [28, 3, 113, 4, 114, ], + 'M': [26, 3, 44, 11, 45, ], + 'Q': [26, 17, 21, 4, 22, ], + 'H': [26, 9, 13, 16, 14, ], + }, + 20: { + 'L': [28, 3, 107, 5, 108, ], + 'M': [26, 3, 41, 13, 42, ], + 'Q': [30, 15, 24, 5, 25, ], + 'H': [28, 15, 15, 10, 16, ], + }, + 21: { + 'L': [28, 4, 116, 4, 117, ], + 'M': [26, 17, 42, 0, 0, ], + 'Q': [28, 17, 22, 6, 23, ], + 'H': [30, 19, 16, 6, 17, ], + }, + 22: { + 'L': [28, 2, 111, 7, 112, ], + 'M': [28, 17, 46, 0, 0, ], + 'Q': [30, 7, 24, 16, 25, ], + 'H': [24, 34, 13, 0, 0, ], + }, + 23: { + 'L': [30, 4, 121, 5, 122, ], + 'M': [28, 4, 47, 14, 48, ], + 'Q': [30, 11, 24, 14, 25, ], + 'H': [30, 16, 15, 14, 16, ], + }, + 24: { + 'L': [30, 6, 117, 4, 118, ], + 'M': [28, 6, 45, 14, 46, ], + 'Q': [30, 11, 24, 16, 25, ], + 'H': [30, 30, 16, 2, 17, ], + }, + 25: { + 'L': [26, 8, 106, 4, 107, ], + 'M': [28, 8, 47, 13, 48, ], + 'Q': [30, 7, 24, 22, 25, ], + 'H': [30, 22, 15, 13, 16, ], + }, + 26: { + 'L': [28, 10, 114, 2, 115, ], + 'M': [28, 19, 46, 4, 47, ], + 'Q': [28, 28, 22, 6, 23, ], + 'H': [30, 33, 16, 4, 17, ], + }, + 27: { + 'L': [30, 8, 122, 4, 123, ], + 'M': [28, 22, 45, 3, 46, ], + 'Q': [30, 8, 23, 26, 24, ], + 'H': [30, 12, 15, 28, 16, ], + }, + 28: { + 'L': [30, 3, 117, 10, 118, ], + 'M': [28, 3, 45, 23, 46, ], + 'Q': [30, 4, 24, 31, 25, ], + 'H': [30, 11, 15, 31, 16, ], + }, + 29: { + 'L': [30, 7, 116, 7, 117, ], + 'M': [28, 21, 45, 7, 46, ], + 'Q': [30, 1, 23, 37, 24, ], + 'H': [30, 19, 15, 26, 16, ], + }, + 30: { + 'L': [30, 5, 115, 10, 116, ], + 'M': [28, 19, 47, 10, 48, ], + 'Q': [30, 15, 24, 25, 25, ], + 'H': [30, 23, 15, 25, 16, ], + }, + 31: { + 'L': [30, 13, 115, 3, 116, ], + 'M': [28, 2, 46, 29, 47, ], + 'Q': [30, 42, 24, 1, 25, ], + 'H': [30, 23, 15, 28, 16, ], + }, + 32: { + 'L': [30, 17, 115, 0, 0, ], + 'M': [28, 10, 46, 23, 47, ], + 'Q': [30, 10, 24, 35, 25, ], + 'H': [30, 19, 15, 35, 16, ], + }, + 33: { + 'L': [30, 17, 115, 1, 116, ], + 'M': [28, 14, 46, 21, 47, ], + 'Q': [30, 29, 24, 19, 25, ], + 'H': [30, 11, 15, 46, 16, ], + }, + 34: { + 'L': [30, 13, 115, 6, 116, ], + 'M': [28, 14, 46, 23, 47, ], + 'Q': [30, 44, 24, 7, 25, ], + 'H': [30, 59, 16, 1, 17, ], + }, + 35: { + 'L': [30, 12, 121, 7, 122, ], + 'M': [28, 12, 47, 26, 48, ], + 'Q': [30, 39, 24, 14, 25, ], + 'H': [30, 22, 15, 41, 16, ], + }, + 36: { + 'L': [30, 6, 121, 14, 122, ], + 'M': [28, 6, 47, 34, 48, ], + 'Q': [30, 46, 24, 10, 25, ], + 'H': [30, 2, 15, 64, 16, ], + }, + 37: { + 'L': [30, 17, 122, 4, 123, ], + 'M': [28, 29, 46, 14, 47, ], + 'Q': [30, 49, 24, 10, 25, ], + 'H': [30, 24, 15, 46, 16, ], + }, + 38: { + 'L': [30, 4, 122, 18, 123, ], + 'M': [28, 13, 46, 32, 47, ], + 'Q': [30, 48, 24, 14, 25, ], + 'H': [30, 42, 15, 32, 16, ], + }, + 39: { + 'L': [30, 20, 117, 4, 118, ], + 'M': [28, 40, 47, 7, 48, ], + 'Q': [30, 43, 24, 22, 25, ], + 'H': [30, 10, 15, 67, 16, ], + }, + 40: { + 'L': [30, 19, 118, 6, 119, ], + 'M': [28, 18, 47, 31, 48, ], + 'Q': [30, 34, 24, 34, 25, ], + 'H': [30, 20, 15, 61, 16, ], + }, +} + +#: This table lists all of the generator polynomials used by QR Codes. +#: They are indexed by the number of "ECC Code Words" (see table above). +#: This table is taken from: +#: +#: http://www.matchadesign.com/blog/qr-code-demystified-part-4/ +generator_polynomials = { + 7: [87, 229, 146, 149, 238, 102, 21], + 10: [251, 67, 46, 61, 118, 70, 64, 94, 32, 45], + 13: [74, 152, 176, 100, 86, 100, 106, 104, 130, 218, 206, 140, 78], + 15: [8, 183, 61, 91, 202, 37, 51, 58, 58, 237, 140, 124, 5, 99, 105], + 16: [120, 104, 107, 109, 102, 161, 76, 3, 91, 191, 147, 169, 182, 194, + 225, 120], + 17: [43, 139, 206, 78, 43, 239, 123, 206, 214, 147, 24, 99, 150, 39, + 243, 163, 136], + 18: [215, 234, 158, 94, 184, 97, 118, 170, 79, 187, 152, 148, 252, 179, + 5, 98, 96, 153], + 20: [17, 60, 79, 50, 61, 163, 26, 187, 202, 180, 221, 225, 83, 239, 156, + 164, 212, 212, 188, 190], + 22: [210, 171, 247, 242, 93, 230, 14, 109, 221, 53, 200, 74, 8, 172, 98, + 80, 219, 134, 160, 105, 165, 231], + 24: [229, 121, 135, 48, 211, 117, 251, 126, 159, 180, 169, 152, 192, 226, + 228, 218, 111, 0, 117, 232, 87, 96, 227, 21], + 26: [173, 125, 158, 2, 103, 182, 118, 17, 145, 201, 111, 28, 165, 53, 161, + 21, 245, 142, 13, 102, 48, 227, 153, 145, 218, 70], + 28: [168, 223, 200, 104, 224, 234, 108, 180, 110, 190, 195, 147, 205, 27, + 232, 201, 21, 43, 245, 87, 42, 195, 212, 119, 242, 37, 9, 123], + 30: [41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110, 86, 239, 96, 222, + 125, 42, 173, 226, 193, 224, 130, 156, 37, 251, 216, 238, 40, 192, + 180] +} + +#: This table contains the log and values used in GF(256) arithmetic. +#: They are used to generate error correction codes for QR Codes. +#: This table is taken from: +#: +#: vhttp://www.thonky.com/qr-code-tutorial/log-antilog-table/ +galois_log = [ + 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, + 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, + 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, 5, + 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, + 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, + 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, + 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, + 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, + 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, 77, + 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, + 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, + 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 130, + 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81, + 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, + 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, + 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1,] + +#: This table contains the antilog and values used in GF(256) arithmetic. +#: They are used to generate error correction codes for QR Codes. +#: This table is taken from: +#: +#: http://www.thonky.com/qr-code-tutorial/log-antilog-table/ +galois_antilog = [ + None, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, + 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, + 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, + 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, 191, + 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, + 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, + 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, + 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, + 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227, 165, + 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, + 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, + 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, 45, + 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, 108, + 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, + 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, + 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175,] + +#: This table contains the coordinates for the position adjustment patterns. +#: The index of the table corresponds to the QR Code's version number. +#: This table is taken from: +#: +#: http://www.thonky.com/qr-code-tutorial/part-3-mask-pattern/ +position_adjustment = [ + None, #There is not version 0 + None, #Version 1 does not need adjustment + [6, 18, ], + [6, 22, ], + [6, 26, ], + [6, 30, ], + [6, 34, ], + [6, 22, 38, ], + [6, 24, 42, ], + [6, 26, 46, ], + [6, 28, 50, ], + [6, 30, 54, ], + [6, 32, 58, ], + [6, 34, 62, ], + [6, 26, 46, 66, ], + [6, 26, 48, 70, ], + [6, 26, 50, 74, ], + [6, 30, 54, 78, ], + [6, 30, 56, 82, ], + [6, 30, 58, 86, ], + [6, 34, 62, 90, ], + [6, 28, 50, 72, 94, ], + [6, 26, 50, 74, 98, ], + [6, 30, 54, 78, 102, ], + [6, 28, 54, 80, 106, ], + [6, 32, 58, 84, 110, ], + [6, 30, 58, 86, 114, ], + [6, 34, 62, 90, 118, ], + [6, 26, 50, 74, 98, 122, ], + [6, 30, 54, 78, 102, 126, ], + [6, 26, 52, 78, 104, 130, ], + [6, 30, 56, 82, 108, 134, ], + [6, 34, 60, 86, 112, 138, ], + [6, 30, 58, 86, 114, 142, ], + [6, 34, 62, 90, 118, 146, ], + [6, 30, 54, 78, 102, 126, 150, ], + [6, 24, 50, 76, 102, 128, 154, ], + [6, 28, 54, 80, 106, 132, 158, ], + [6, 32, 58, 84, 110, 136, 162, ], + [6, 26, 54, 82, 110, 138, 166, ], + [6, 30, 58, 86, 114, 142, 170, ], +] + +#: This table specifies the bit pattern to be added to a QR Code's +#: image to specify what version the code is. Note, this pattern +#: is not used for versions 1-6. This table is taken from: +#: +#: http://www.thonky.com/qr-code-tutorial/part-3-mask-pattern/ +version_pattern = [None, None, None, None, None, None, None, #0-6 + '000111110010010100', '001000010110111100', '001001101010011001', + '001010010011010011', '001011101111110110', '001100011101100010', + '001101100001000111', '001110011000001101', '001111100100101000', + '010000101101111000', '010001010001011101', '010010101000010111', + '010011010100110010', '010100100110100110', '010101011010000011', + '010110100011001001', '010111011111101100', '011000111011000100', + '011001000111100001', '011010111110101011', '011011000010001110', + '011100110000011010', '011101001100111111', '011110110101110101', + '011111001001010000', '100000100111010101', '100001011011110000', + '100010100010111010', '100011011110011111', '100100101100001011', + '100101010000101110', '100110101001100100', '100111010101000001', + '101000110001101001' +] + +#: This table contains the bit fields needed to specify the error code level and +#: mask pattern used by a QR Code. This table is take from: +#: +#: http://www.thonky.com/qr-code-tutorial/part-3-mask-pattern/ +type_bits = { + 'L': { + 0: '111011111000100', + 1: '111001011110011', + 2: '111110110101010', + 3: '111100010011101', + 4: '110011000101111', + 5: '110001100011000', + 6: '110110001000001', + 7: '110100101110110', + }, + 'M': { + 0: '101010000010010', + 1: '101000100100101', + 2: '101111001111100', + 3: '101101101001011', + 4: '100010111111001', + 5: '100000011001110', + 6: '100111110010111', + 7: '100101010100000', + }, + 'Q': { + 0: '011010101011111', + 1: '011000001101000', + 2: '011111100110001', + 3: '011101000000110', + 4: '010010010110100', + 5: '010000110000011', + 6: '010111011011010', + 7: '010101111101101', + }, + 'H': { + 0: '001011010001001', + 1: '001001110111110', + 2: '001110011100111', + 3: '001100111010000', + 4: '000011101100010', + 5: '000001001010101', + 6: '000110100001100', + 7: '000100000111011', + }, +} + +#: This table contains *functions* to compute whether to change current bit when +#: creating the masks. All of the functions in the table return a boolean value. +#: A True result means you should add the bit to the QR Code exactly as is. A +#: False result means you should add the opposite bit. This table was taken +#: from: +#: +#: http://www.thonky.com/qr-code-tutorial/mask-patterns/ +mask_patterns = [ + lambda row, col: (row + col) % 2 == 0, + lambda row, col: row % 2 == 0, + lambda row, col: col % 3 == 0, + lambda row, col: (row + col) % 3 == 0, + lambda row, col: ((row // 2) + (col // 3)) % 2 == 0, + lambda row, col: ((row * col) % 2) + ((row * col) % 3) == 0, + lambda row, col: (((row * col) % 2) + ((row * col) % 3)) % 2 == 0, + lambda row, col: (((row + col) % 2) + ((row * col) % 3)) % 2 == 0] + + +#: This is a table of ASCII escape code for terminal colors. QR codes +#: are drawn using a space with a colored background. Hence, only +#: codes affecting background colors have been added. +#: http://misc.flogisoft.com/bash/tip_colors_and_formatting +term_colors = { + 'default': 49, + 'background': 49, + + 'reverse': 7, + 'reversed': 7, + 'inverse': 7, + 'inverted': 7, + + 'black': 40, + 'red': 41, + 'green': 42, + 'yellow': 43, + 'blue': 44, + 'magenta': 45, + 'cyan': 46, + 'light gray': 47, + 'light grey': 47, + 'dark gray': 100, + 'dark grey': 100, + 'light red': 101, + 'light green': 102, + 'light blue': 103, + 'light yellow': 104, + 'light magenta': 105, + 'light cyan': 106, + 'white': 107 +} diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..861a9f5 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..9fac429 --- /dev/null +++ b/setup.py @@ -0,0 +1,76 @@ +# Copyright (c) 2013, Michael Nooner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from setuptools import setup +import sys, os.path, shutil + +version = '1.2.1' + +if sys.version_info < (2, 6, 0) and sys.version_info < (3, 0, 0): + sys.stderr.write("pyqrcode requires Python 2.6+ or 3.\n") + sys.exit(1) + + +#Make the README.rst file the long description +#This only happens when we are building from the +#source. +if os.path.exists('docs/README.rst'): + print('Reading README.rst file') + with open( 'docs/README.rst', 'r') as f: + longdesc = f.read() + shutil.copyfile('docs/README.rst', 'README.rst') +else: + longdesc = None + +setup(name='PyQRCode', + packages=['pyqrcode'], + version=version, + description='A QR code generator written purely in Python with SVG, EPS, PNG and terminal output.', + author='Michael Nooner', + author_email='mnooner256@gmail.com', + url='https://github.com/mnooner256/pyqrcode', + keywords=['qrcode', 'qr'], + license='BSD', + extras_require = { + 'PNG': ["pypng>=0.0.13"], + }, + classifiers = [ + 'Development Status :: 4 - Beta', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Topic :: Software Development :: Libraries :: Python Modules', + 'Natural Language :: English', + 'Operating System :: OS Independent', + 'Operating System :: POSIX', + 'Operating System :: Microsoft :: Windows', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + ], + long_description=longdesc, +) + +if os.path.exists('docs/README.rst'): + os.remove('README.rst')