[
  {
    "path": ".gitignore",
    "content": "# Pycharm files\n.idea/\n\n# personal test file\ntest.py\nexample.jpg\n\n# Created by .ignore support plugin (hsz.mobi)\n### Python template\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nenv/\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*,cover\n.hypothesis/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# IPython Notebook\n.ipynb_checkpoints\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# dotenv\n.env\n\n# virtualenv\nvenv/\nENV/\n\n# Spyder project settings\n.spyderproject\n\n# Rope project settings\n.ropeproject\n\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\nCopyright (c) 2016 7sDream\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# PyQArt - QArt Python implementation\n\n[中文版 README](https://github.com/7sDream/pyqart/blob/master/README.zh.md)\n\n## introduction\n\nQArt is a method of combining QrCode of an URL with any image, which was submitted in [an article][qart-article] writen by [Russ Cox][russ-cos-google-plus] on his personal website.\n\nAn Example(come from the article):\n\n![QArt Example][qart-example]\n\nThis repo is Python implementation of it.\n\n## Install\n\n```\npip install pyqart\n```\n\n**Note: Support Python3 only, please make sure you are using pip of Python3.**\n\n## Usage\n\nFor code reuse, I split the lib to two part. One for generate normal QrCode, another for generate QArt.\n\n### The Qr Part\n\nUse `pyqr` CLI to create normal QrCode.\n\n```\n pyqr -p 5 -c 102 204 255 \"Hello World!\" -o qr.png\n ```\n \n The options:\n \n - `-p` for point size of QrQCode, by pixel, default is 3 pixel.\n - `-c` for color of point, default is black. Background color can be set with `-g` option, default is white.\n\n![qr code: hello world][my-qr-img]\n\nIf you want show it in terminal, just don't provide `-o` option:\n\n```bash\npyqr \"Hello World\"\n```\n\nThen you will see:\n\n![qr in terminal: hello world][my-qr-terminal]\n\nThe actual result you will see depends on your font setting, I'm using Dejavu Sans Mono.\n\nYes, it is only useful for small QrCode. \n\nRun `pyqr -h` for more options and their effect.\n\n### The Art Part\n\nUse `pyqart` CLI to create QArt. It may take a long time, please be patient :)\n\nThis is an example that mix my blog url and my Github avatar: \n\n```\npyqart -v 8 -c 102 204 255 \"http://0v0.link/\" photo.jpg -o qart.png\n```\n\nMy Github avatar:\n\n![][my-github-avatar]\n\nThe QArt Code:\n\n![][my-qart-img]\n\nNot meet your expectations? Try `-n` option to pick point at random(default is pick low-contrast region pixels first):\n\n```bash\npyqart -n -c 102 204 255 -v 8 \"http://0v0.link/\" photo.jpg -o qart-n.png\n```\n\n![][my-qart-n-img]\n\nStill not satisfied? Use `-y` option to enhance the accuracy of the central region by giving up the control of the edge pixels:\n\n![][my-qart-y-img]\n\n`-y` and `-n` can be used at the same time, but no obvious improvement.\n\n**Note： because that `-y` option will only use data block, ignore error correction block，it reduce many many many calculate. It has about 30x to 100x speed up compare with no `-y` option case. So I strongly recommend using `-y` option whenever you needn't make a full picture fitting.**\n\nUse `-r` option to set rotation degree, The controllable data region can be changed into a horizontal area, it will make it easier to process very wide picture.\n\n![][my-pyqart-y-r-img]\n\nRun `pyqart -h` for more options and their effect.\n\n### Use it in your codes as a module\n\nDocumentation is in preparation.\n\n## Gallery\n\n![][python-qr]\n\npython.org(used -d option, means dithering, see help message for more info.)\n\n![][github-qr]\n\ngithub.com\n\n![][bilibili-qr]\n\nbilibili.com (An ACG videos website)\n\n## Halftone and HalfArt support\n\nHalftone support added in version 0.1.0, and I made another new method which combined Halftone and QArt, so I call it HalfArt temporarily.\n\n## Arguments for all methods\n\nThe following code shows arguments to get output image of all kind of method:\n\n```python\nfrom pyqart import QArtist, QrHalftonePrinter, QrImagePrinter, QrPainter\n\nQR_VERSION = 10\nPOINT_PIXEL = 3\n\nartist = QArtist('http://www.nankai.edu.cn/', 'example.jpg', QR_VERSION)\npainter = QrPainter('http://www.nankai.edu.cn/', QR_VERSION)\nartist_data_only = QArtist('http://www.nankai.edu.cn/', 'example.jpg',\n                           QR_VERSION, only_data=True)\n\n# normal\nQrImagePrinter.print(painter, path='normal.png', point_width=POINT_PIXEL)\n# Halftone\nQrHalftonePrinter.print(painter, path='halftone.png', img='example.jpg',\n                        point_width=POINT_PIXEL, colorful=False)\n# Halftone colorful\nQrHalftonePrinter.print(painter, path='halftone-color.png', img='example.jpg',\n                        point_width=POINT_PIXEL)\n# Halftone pixel\nQrHalftonePrinter.print(painter, path='halftone-pixel.png', img='example.jpg',\n                        point_width=POINT_PIXEL, colorful=False,\n                        pixelization=True)\n# QArt\nQrImagePrinter.print(artist, path='qart.png', point_width=POINT_PIXEL)\n# QArt data only\nQrImagePrinter.print(artist_data_only, path='qart-data-only.png',\n                     point_width=POINT_PIXEL)\n# HalfArt\nQrHalftonePrinter.print(artist, path='halfart.png', point_width=POINT_PIXEL)\n# HalfArt data only\nQrHalftonePrinter.print(artist_data_only, path='halfart-data-only.png',\n                        point_width=POINT_PIXEL)\n```\n\n### Result example for all method\n\n|  |  |  |\n| :-: | :-: | :-: |\n| ![][halftone.png]| ![][halftone-color.png] | ![][halftone-pixel.png] |\n| Halftone | Halftone colorful | Halftone pixel |\n| ![][qart.png] | ![][qart-data-only.png] | |\n| QArt | QArt data only | |\n| ![][halfart.png] | ![][halfart-data-only.png] | |\n| HalfArt | HalfArt data only | |\n\n## TODO\n\n- [x] Make QrPainter decided argument by itself.\n- [x] Art part\n- [x] CLI\n- [x] Package\n- [x] Halftone support\n- [x] self-made HalfArt method\n- [ ] GUI\n- [ ] Use Cython to accelerate Reed-Solomon error correction\n- [ ] Docs\n- [ ] Tests\n\n## Other Implementation\n\n- Golang: [qr][qr] by [Russ Cox][russ-cos-google-plus]\n- Java: [qart4j][qart4j] by [dieforfree][dieforfree]\n\n## Acknowledgements\n\n- All credit goes to [Russ Cos][russ-cos-google-plus], Thanks for his article and implement.\n- Thanks for [qart4j project][qart4j] by [dieforfree][dieforfree]，which helps me so much on how to implement the art part.\n- Thanks to a series of articles named [QR Code Tutorial][tutorial] in thonky.com, It's very detailed. Whenever I faced problem about encoding or error correction, I will go to it for help.\n- Thanks to the Python programing language。\n\n## LICENSE\n\nMIT.\n\nSee LICENSE.\n\n[russ-cos-google-plus]: https://plus.google.com/+RussCox-rsc\n[qart-article]: http://research.swtch.com/qart\n[qart-example]: http://ww4.sinaimg.cn/large/88e401f0gw1f6dl845naoj205g05ga9y.jpg\n[my-qr-img]: http://ww3.sinaimg.cn/large/88e401f0gw1f6ir3ifivzj20370370ss.jpg\n[my-qr-terminal]: http://ww2.sinaimg.cn/large/88e401f0gw1f6ir4taf7hj209008c3ze.jpg\n[my-github-avatar]: http://ww3.sinaimg.cn/large/88e401f0gw1f6iyj9nuwhj2049049q2v.jpg\n[my-qart-img]: http://ww3.sinaimg.cn/large/88e401f0gw1f6ir8t0mbej20490490t2.jpg\n[my-qart-n-img]: http://ww1.sinaimg.cn/large/88e401f0gw1f6irh15ouuj2049049mxp.jpg\n[my-qart-y-img]: http://ww2.sinaimg.cn/large/88e401f0gw1f6irbnfjozj20490490t4.jpg\n[my-pyqart-y-r-img]: http://ww3.sinaimg.cn/large/88e401f0gw1f6jd7w10r7j205l05lt91.jpg\n[qr]: https://code.google.com/p/rsc/source/browse/qr\n[dieforfree]: https://github.com/dieforfree\n[qart4j]: https://github.com/dieforfree/qart4j\n[tutorial]: http://www.thonky.com/qr-code-tutorial/\n\n[python-qr]: http://ww1.sinaimg.cn/large/88e401f0gw1f6iz81tkwpj204x04xaaf.jpg\n[github-qr]: http://ww4.sinaimg.cn/large/88e401f0gw1f6izdtv2kqj204x04x0sy.jpg\n[bilibili-qr]: http://ww3.sinaimg.cn/large/88e401f0gw1f6j0ds93k9j204x04x74m.jpg\n\n[halftone.png]: http://rikka-10066868.image.myqcloud.com/f62cbc2f-1e38-4a94-80aa-0be1a0c32b55.png\n[halftone-color.png]: http://rikka-10066868.image.myqcloud.com/d96d057a-42d2-469b-9b65-0eabd2bd915f.png\n[halftone-pixel.png]: http://rikka-10066868.image.myqcloud.com/00da6fa8-5035-4ba6-8c33-584b54e73e2d.png\n[qart.png]: http://rikka-10066868.image.myqcloud.com/d2f3febb-a535-4154-8ebc-80183701c47d.png\n[qart-data-only.png]: http://rikka-10066868.image.myqcloud.com/59834cea-5d44-41c3-b759-780c56c9789b.png\n[halfart.png]: http://rikka-10066868.image.myqcloud.com/8b0847b9-c3fc-451d-b554-7bdc3a53f7e9.png\n[halfart-data-only.png]: http://rikka-10066868.image.myqcloud.com/9f4fd92e-99ff-4aca-a252-b6c1ab709e65.png\n"
  },
  {
    "path": "README.zh.md",
    "content": "# PyQArt - QArt 的  Python 实现\n\n[Readme in English](https://github.com/7sDream/pyqart/blob/master/README.md)\n\n## 简介\n\nQArt 是由 [Russ Cox][russ-cos-google-plus] 在他个人网站的[一篇文章][qart-article]中提出的一种将包含 URL 的二维码与图像结合的方法。\n\n示例图片（来源于 Russ Cox 的文章）：\n\n![QArt Example][qart-example]\n\n这个库是 QArt 的 Python 实现版本。\n\n## 安装\n\n```bash\npip install pyqart\n```\n\n**注：只支持 Python3，请确认你使用的是 python3 版本的 pip。**\n\n## 使用\n\n为便于重用，我将库分成了两部分，一部分是普通二维码生成，另一部分则将 URL 二维码与图像结合。\n\n### Qr 部分\n\n使用 `pyqr` 命令行程序，可以创建普通二维码。\n\n```\n pyqr -p 5 -c 102 204 255 \"Hello World!\" -o qr.png\n```\n\n其中：\n\n- `-p` 参数指定生成的二维码图片中，每个填充点的大小，默认是 3 像素。\n- `-c` 参数指定填充点的颜色，默认是黑色。背景色默认为白色，可以用 `-g` 参数设定。\n\n![qr code: hello world][my-qr-img]\n\n如果你想在终端里查看的话，不提供 `-o` 参数即可：\n\n```bash\npyqr \"Hello World\"\n```\n\n输出如下：\n\n![qr in terminal: hello world][my-qr-terminal]\n\n显示效果和你终端的字体有关，我的字体是 Dejavu Sans Mono.\n\n当然，终端只能用于显示比较小的二维码。\n\n有关二维码生成的更多参数和它们的作用请使用 `pyqr -h` 命令查看。\n\n### Art 部分\n\n使用 `pyqart` 命令行程序创建艺术二维码。（所需时间可能较长，请耐心等待）\n\n使用我的博客网址和 Github 头像来做例子，`-v` 参数指定二维码的大小。\n\n```\npyqart -v 8 -c 102 204 255 \"http://0v0.link/\" photo.jpg -o qart.png\n```\n\n这是我的 Github 头像：\n\n![][my-github-avatar]\n\n生成的二维码如下，扫描一下就会跳转到我的博客啦：\n\n![][my-qart-img]\n\n可能效果不太好，试试使用 `-n` 参数来随机选取像素点（默认情况下会先处理大片相同颜色的区域）：\n\n```bash\npyqart -n -c 102 204 255 -v 8 \"http://0v0.link/\" photo.jpg -o qart-n.png\n```\n\n![][my-qart-n-img]\n\n可能还是不太好？再试试 `-y` 参数，它通过放弃边缘区域来加强中间区域的逼近效果：\n\n```bash\npyqart -y -c 102 204 255 -v 8 \"http://0v0.link/\" photo.jpg -o qart-y.png\n```\n\n![][my-qart-y-img]\n\n`-y` 和 `-n` 参数也可以结合起来使用，不过提升不会很明显。\n\n**注意： `-y` 参数由于只只使用数据块而不使用纠错块，减少了很多很多很多操作，相比没有 `-y` 参数大概有 30 到 100 倍的速度提升，强烈建议在不需要全图拟合时使用 `-y` 参数。**\n\n另外，使用 `-r` 参数指定二维码的旋转角度，可以把可控制的数据区变为横向，方便扁长图形处理：\n\n![][my-pyqart-y-r-img]\n\n有关 QArt 生成的更多参数和它们的作用请使用 `pyqart -h` 命令查看。\n\n### 作为模块使用\n\n文档正在编写中。\n\n## 更多示例\n\n![][python-qr]\n\nPython 官网。(此示例使用了 -d 参数，请查看帮助获取更多信息)\n\n![][github-qr]\n\nGithub 首页。\n\n![][bilibili-qr]\n\n哔哩哔哩。\n\n## Halftone 和 HalfArt 支持\n\n0.1.0 版本增加了 Halftone 支持，另外也实现了一种结合了 QArt 和 Halftone 的新算法，我暂时命名为 HalfArt。\n\n## 各方法所用参数\n\n以下代码展示了输出各种格式的所需参数：\n\n```python\nfrom pyqart import QArtist, QrHalftonePrinter, QrImagePrinter, QrPainter\n\nQR_VERSION = 10\nPOINT_PIXEL = 3\n\nartist = QArtist('http://www.nankai.edu.cn/', 'example.jpg', QR_VERSION)\npainter = QrPainter('http://www.nankai.edu.cn/', QR_VERSION)\nartist_data_only = QArtist('http://www.nankai.edu.cn/', 'example.jpg',\n                           QR_VERSION, only_data=True)\n\n# normal\nQrImagePrinter.print(painter, path='normal.png', point_width=POINT_PIXEL)\n# Halftone\nQrHalftonePrinter.print(painter, path='halftone.png', img='example.jpg',\n                        point_width=POINT_PIXEL, colorful=False)\n# Halftone colorful\nQrHalftonePrinter.print(painter, path='halftone-color.png', img='example.jpg',\n                        point_width=POINT_PIXEL)\n# Halftone pixel\nQrHalftonePrinter.print(painter, path='halftone-pixel.png', img='example.jpg',\n                        point_width=POINT_PIXEL, colorful=False,\n                        pixelization=True)\n# QArt\nQrImagePrinter.print(artist, path='qart.png', point_width=POINT_PIXEL)\n# QArt data only\nQrImagePrinter.print(artist_data_only, path='qart-data-only.png',\n                     point_width=POINT_PIXEL)\n# HalfArt\nQrHalftonePrinter.print(artist, path='halfart.png', point_width=POINT_PIXEL)\n# HalfArt data only\nQrHalftonePrinter.print(artist_data_only, path='halfart-data-only.png',\n                        point_width=POINT_PIXEL)\n```\n\n### 各方法结果样例\n\n|  |  |  |\n| :-: | :-: | :-: |\n| ![][halftone.png]| ![][halftone-color.png] | ![][halftone-pixel.png] |\n| Halftone | Halftone colorful | Halftone pixel |\n| ![][qart.png] | ![][qart-data-only.png] | |\n| QArt | QArt data only | |\n| ![][halfart.png] | ![][halfart-data-only.png] | |\n| HalfArt | HalfArt data only | |\n\n## TODO\n\n- [x] 让 QrPainter 能自己决定参数\n- [x] Art 部分\n- [x] CLI\n- [x] 打包\n- [x] Halftone 支持\n- [x] 自制 HalfArt 方法\n- [ ] GUI\n- [ ] 使用 Cython 加快里德所罗门码编码速度\n- [ ] 文档\n- [ ] 测试\n\n## 其他实现版本\n\n- Golang: [qr][qr] by [Russ Cox][russ-cos-google-plus]\n- Java: [qart4j][qart4j] by [dieforfree][dieforfree]\n\n## 致谢\n\n- 所有一切都源自 [Russ Cos][russ-cos-google-plus] 的文章，感谢他。\n- 感谢 [dieforfree][dieforfree] 的 [qart4j 项目][qart4j]，它给我提供了很多如何实现 art 部分参考。\n- 感谢 thonky.com 的 [二维码原理指导][tutorial] 系列文章，非常详细，关于编码和纠错中不懂的地方多亏了它。\n- 感谢 Python。\n\n## 协议\n\nMIT。\n\n参见 LICENSE 文件。\n\n[russ-cos-google-plus]: https://plus.google.com/+RussCox-rsc\n[qart-article]: http://research.swtch.com/qart\n[qart-example]: http://ww4.sinaimg.cn/large/88e401f0gw1f6dl845naoj205g05ga9y.jpg\n[my-qr-img]: http://ww3.sinaimg.cn/large/88e401f0gw1f6ir3ifivzj20370370ss.jpg\n[my-qr-terminal]: http://ww2.sinaimg.cn/large/88e401f0gw1f6ir4taf7hj209008c3ze.jpg\n[my-github-avatar]: http://ww3.sinaimg.cn/large/88e401f0gw1f6iyj9nuwhj2049049q2v.jpg\n[my-qart-img]: http://ww3.sinaimg.cn/large/88e401f0gw1f6ir8t0mbej20490490t2.jpg\n[my-qart-n-img]: http://ww1.sinaimg.cn/large/88e401f0gw1f6irh15ouuj2049049mxp.jpg\n[my-qart-y-img]: http://ww2.sinaimg.cn/large/88e401f0gw1f6irbnfjozj20490490t4.jpg\n[my-pyqart-y-r-img]: http://ww3.sinaimg.cn/large/88e401f0gw1f6jd7w10r7j205l05lt91.jpg\n[qr]: https://code.google.com/p/rsc/source/browse/qr\n[dieforfree]: https://github.com/dieforfree\n[qart4j]: https://github.com/dieforfree/qart4j\n[tutorial]: http://www.thonky.com/qr-code-tutorial/\n\n[python-qr]: http://ww1.sinaimg.cn/large/88e401f0gw1f6iz81tkwpj204x04xaaf.jpg\n[github-qr]: http://ww4.sinaimg.cn/large/88e401f0gw1f6izdtv2kqj204x04x0sy.jpg\n[bilibili-qr]: http://ww3.sinaimg.cn/large/88e401f0gw1f6j0ds93k9j204x04x74m.jpg\n\n[halftone.png]: http://rikka-10066868.image.myqcloud.com/f62cbc2f-1e38-4a94-80aa-0be1a0c32b55.png\n[halftone-color.png]: http://rikka-10066868.image.myqcloud.com/d96d057a-42d2-469b-9b65-0eabd2bd915f.png\n[halftone-pixel.png]: http://rikka-10066868.image.myqcloud.com/00da6fa8-5035-4ba6-8c33-584b54e73e2d.png\n[qart.png]: http://rikka-10066868.image.myqcloud.com/d2f3febb-a535-4154-8ebc-80183701c47d.png\n[qart-data-only.png]: http://rikka-10066868.image.myqcloud.com/59834cea-5d44-41c3-b759-780c56c9789b.png\n[halfart.png]: http://rikka-10066868.image.myqcloud.com/8b0847b9-c3fc-451d-b554-7bdc3a53f7e9.png\n[halfart-data-only.png]: http://rikka-10066868.image.myqcloud.com/9f4fd92e-99ff-4aca-a252-b6c1ab709e65.png\n"
  },
  {
    "path": "pyqart/__init__.py",
    "content": "from .qr import (\n    QrData,\n    QrPainter,\n    # ------------\n    QrBasePrinter,\n    QrImagePrinter,\n    QrStringPrinter,\n    QrHalftonePrinter,\n    # ------------\n    QrException,\n)\n\nfrom .art import QArtist\n\n__version__ = '0.1.0'\n"
  },
  {
    "path": "pyqart/art/__init__.py",
    "content": "from .qart import QArtist\n"
  },
  {
    "path": "pyqart/art/bitblock.py",
    "content": "# Added at : 2016.8.3\n# Author   : 7sDream\n# Usage    : A block of bits. In this class, we let each point (including EC)\n#               as much as possible to meet the color of image in it's position.\n\nfrom ..common import Bits, one_at, bit_at, BIT_PER_CW\nfrom ..qr.ec import RSEncoder\n\n__all__ = ['BitBlock']\n\n_VS_CACHE = {}\n\n\ndef _copy(i):\n    vs = []\n    for line in _VS_CACHE[i]:\n        vs.append([x for x in line])\n    return vs\n\n\ndef _create_vs(dbc, ecbc):\n    if dbc not in _VS_CACHE:\n        print('can\\'t find in cache, calculating...', end='', flush=True)\n        vs = []\n        for i in range(dbc):\n            b = [0] * (i // 8) + [one_at(i % 8)] + [0] * (dbc // 8 - i // 8 - 1)\n            b += RSEncoder.encode(b, ecbc // BIT_PER_CW, False)\n            vs.append(b)\n        print('Done')\n        _VS_CACHE[dbc] = vs\n    else:\n        print('found in cache.')\n    return _copy(dbc)\n\n\nclass BitBlock(object):\n    def __init__(self, bits, di, dbc, eci, ecbc):\n        self._dbc = dbc\n        self._bits = Bits.copy_from(bits, di, dbc)\n        self._bits.extend(bits, eci, ecbc)\n        self._bits = self._bits.as_int_list\n\n        # vector space\n        self._vs = _create_vs(dbc, ecbc)\n\n        self._locked_index = len(self._vs)\n        self._already_set = set()\n        self._max_index = len(self._bits) * 8\n\n    def set(self, index, value):\n        assert isinstance(index, int)\n        assert isinstance(value, bool)\n        assert 0 <= index < self._max_index\n\n        if index in self._already_set:\n            return False\n        if len(self._already_set) >= self._dbc:\n            return False\n\n        found = False\n\n        for i in range(self._locked_index):\n            if bit_at(self._vs[i][index // 8], 8, index % 8) is False:\n                continue\n            if not found:\n                found = True\n                if i != 0:\n                    self._exchange_row(0, i)\n                continue\n            self._vs_xor_line(i, 0)\n\n        if not found:\n            return False\n\n        for i in range(self._locked_index, len(self._vs)):\n            if bit_at(self._vs[i][index // 8], 8, index % 8) is True:\n                self._vs_xor_line(i, 0)\n\n        if bit_at(self._bits[index // 8], 8, index % 8) is not value:\n            self._bits_xor_with_vs(0)\n\n        self._exchange_row(0, self._locked_index - 1)\n        self._locked_index -= 1\n        self._already_set.add(index)\n\n        return True\n\n    def bits(self):\n        return Bits.copy_from(bytearray(self._bits))\n\n    def _vs_xor_line(self, i, j):\n        self._vs[i] = [a ^ b for a, b in zip(self._vs[i], self._vs[j])]\n\n    def _bits_xor_with_vs(self, i):\n        self._bits = [a ^ b for a, b in zip(self._bits, self._vs[i])]\n\n    def _exchange_row(self, i, j):\n        self._vs[i], self._vs[j] = self._vs[j], self._vs[i]\n"
  },
  {
    "path": "pyqart/art/exception.py",
    "content": "# Added at : 2016.8.2\n# Author   : 7sDream\n# Usage    : Exceptions happened at art part.\n\nfrom ..qr import QrException\n\n__all__ = ['ArtException']\n\n\nclass ArtException(QrException):\n    pass\n"
  },
  {
    "path": "pyqart/art/qart.py",
    "content": "# Added at : 2016.8.2\n# Author   : 7sDream\n# Usage    : Accept data and source image, make a QArt.\n\nimport itertools\nfrom random import randint\n\nfrom .source import QArtSourceImage\nfrom .bitblock import BitBlock\nfrom ..qr import QrData, QrPainter\nfrom ..qr.data.numbers import Numbers\nfrom ..qr.painter.point import QrPointType\nfrom ..qr.ec import RSEncoder\nfrom ..common import Bits, BIT_PER_CW\n\n__all__ = ['QArtist']\n\nINF = float('inf')\n\n\nclass QArtist(QrPainter):\n    def __init__(self, url, img, version=None, mask=None, level=0, rotation=0,\n                 dither=False, only_data=False, rand=False, higher_first=False,\n                 dy=None, dx=None):\n        assert isinstance(img, (str, QArtSourceImage))\n        if isinstance(img, str):\n            img = QArtSourceImage(img)\n        self.source = img\n        self.dy = dy\n        self.dx = dx\n        self._only_data = bool(only_data)\n        self._higher_first = bool(higher_first)\n        data = QrData(url + '#', level)\n        super().__init__(data, version, mask, rotation)\n        args, _, _ = self.get_params()\n        print('Processing input image...', end='', flush=True)\n        self._targets = self.source.to_targets(\n            self.canvas, args, bool(dither), rand, dy, dx)\n        self.dither = dither\n        print('Done.')\n        self._bits = None\n\n    @property\n    def bits(self):\n        if self._bits is not None:\n            return self._bits\n        args, available, used = self.get_params()\n        cci_length = args.cci_length_of(Numbers)\n        available_for_number = available - 4 - cci_length\n        used += 4 + cci_length\n        if available_for_number < 4:\n            return super().bits\n        else:\n            numbers_count = available_for_number // 10 * 3\n            remaining = available_for_number % 10\n            if remaining >= 7:\n                numbers_count += 2\n                remaining -= 7\n            elif remaining >= 4:\n                numbers_count += 1\n                remaining -= 4\n            upper = args.dcwc * BIT_PER_CW - remaining\n\n        self._data.put_numbers('0' * numbers_count)\n\n        while True:\n            bits = super().bits\n            di = 0\n            eci = args.dcwc * BIT_PER_CW\n            ecbc = args.eccwcpb * BIT_PER_CW\n            data_bits = Bits()\n            ec_bits = Bits()\n\n            for i in range(args.bc):\n                dbc = args.dcwcof(i) * BIT_PER_CW\n                low = 0\n                high = dbc\n                if di < used:\n                    low = used - di\n                    if low >= dbc:\n                        data_bits.extend(bits, di, dbc)\n                        ec_bits.extend(bits, eci, ecbc)\n                        di += dbc\n                        eci += ecbc\n                        continue\n\n                if di + dbc > upper:\n                    high = upper - di\n                    if high <= 0:\n                        data_bits.extend(bits, di, dbc)\n                        ec_bits.extend(bits, eci, ecbc)\n                        di += dbc\n                        eci += ecbc\n                        continue\n\n                if not self._only_data:\n                    print('Create BitBlock', '{i}/{bc}...'.format(\n                        i=i+1, bc=args.bc,\n                    ), end='', flush=True)\n                    block = BitBlock(bits, di, dbc, eci, ecbc)\n                else:\n                    block = Bits.copy_from(bits, di, dbc)\n\n                # Lock uncontrollable bits\n\n                locked_bits = set()\n\n                if not self._only_data:\n                    for j in itertools.chain(range(0, low), range(high, dbc)):\n                        assert block.set(j, bits[di + j])\n                else:\n                    for j in itertools.chain(range(0, low), range(high, dbc)):\n                        locked_bits.add(j)\n\n                targets_index = list(range(di, di+dbc))\n                if not self._only_data:\n                    targets_index.extend(range(eci, eci+ecbc))\n\n                def compare(x):\n                    t = self._targets[x]\n                    if t.is_hard_zero():\n                        if self._higher_first:\n                            return INF\n                        else:\n                            return -1\n                    else:\n                        return t.contrast\n\n                targets_index = sorted(targets_index, key=compare,\n                                       reverse=self._higher_first)\n\n                for target_index in targets_index:\n                    target = self._targets[target_index]\n                    point = target.point\n                    fill = target.fill\n                    if point.invert:\n                        fill = not fill\n                    if target.is_hard_zero():\n                        fill = False\n                    if point.type is QrPointType.DATA:\n                        index = point.offset - di\n                    else:\n                        assert point.type is QrPointType.CORRECTION\n                        index = point.offset - eci + dbc\n                    if not self._only_data:\n                        block.set(index, fill)\n                    elif index not in locked_bits:\n                        block[index] = fill\n\n                if not self._only_data:\n                    new_block_bits = block.bits()\n                    data_bits.extend(new_block_bits, 0, dbc)\n                    ec_bits.extend(new_block_bits, dbc, ecbc)\n                else:\n                    data_bits.extend(block)\n                    ec_bits.extend(RSEncoder.encode(block, ecbc // 8, True))\n\n                di += dbc\n                eci += ecbc\n\n            error_count = 0\n\n            numbers = ''\n            for i in range(0, numbers_count, 3):\n                if i + 3 > numbers_count:\n                    count = [None, 4, 7][numbers_count - i]\n                else:\n                    count = 10\n                offset = used + i // 3 * 10\n                value = Bits.copy_from(data_bits, offset, count)\n                value = value.as_int\n                if count == 10 and value >= 1000:\n                    rand_pos = randint(0, 4)\n                    hard_zero_pos = offset + rand_pos\n                    self._targets[hard_zero_pos].set_hard_zero()\n                    error_count += 1\n                    value -= 2**(9 - rand_pos)\n                elif count == 7 and value >= 100:\n                    rand_pos = randint(0, 1)\n                    hard_zero_pos = offset + rand_pos\n                    self._targets[hard_zero_pos].set_hard_zero()\n                    error_count += 1\n                    value -= 2**(6 - rand_pos)\n                elif count == 4 and value >= 10:\n                    hard_zero_pos = offset\n                    self._targets[hard_zero_pos].set_hard_zero()\n                    error_count += 1\n                    value -= 8\n                numbers += str(value).rjust(count // 3, '0')\n\n            print('Error count', error_count, end='')\n            if error_count == 0:\n                print(', send to printer.')\n                data_bits.extend(ec_bits)\n                self._bits = data_bits\n                return data_bits\n            else:\n                print(', restart.')\n"
  },
  {
    "path": "pyqart/art/source.py",
    "content": "# Added at : 2016.8.2\n# Author   : 7sDream\n# Usage    : Source image to make QArt.\n\nfrom random import randint\n\nimport PIL.Image as Image\n\nfrom .target import Target\nfrom ..qr.painter.point import QrPointType\n\n__all__ = ['QArtSourceImage']\n\n\nclass QArtSourceImage(object):\n    def __init__(self, path, left=None, top=None, size=None, board=None):\n        \"\"\"\n        :param str|file path: Image file path or file-like object.\n        :param int left: X of start point.\n        :param int top: Y of start point.\n        :param int size: Size of target image region.\n        :param int board: Board region width.\n        \"\"\"\n        self._img = Image.open(path)\n        left = left or 0\n        top = top or 0\n        if size is None:\n            size = min(self._img.width - left, self._img.height - top)\n        self._set(left or 0, top or 0, size or 0, board or 0)\n        self.path = path\n\n    def _set(self, left, top, size, border):\n        assert left >= 0, \"left arg must > 0\"\n        assert top >= 0, \"top arg must > 0\"\n        assert size >= 0, \"size arg must >= 0\"\n        assert left + size <= self._img.width, \"region over image\"\n        assert top + size <= self._img.height, \"region over image\"\n        assert border >= 0, \"border width must >= 0\"\n        self._left = int(left)\n        self._top = int(top)\n        self._size = int(size)\n        self._border = int(border)\n\n    def set_by_center(self, x, y, size, board):\n        offset = (size - 1) // 2\n        self._set(x - offset, y - offset, size, board)\n\n    @staticmethod\n    def _calc_divider(img):\n        res = 0\n        for row in range(img.height):\n            for col in range(img.width):\n                res += img.getpixel((row, col))\n        n = img.width * img.height\n\n        if n == 0:\n            return 128\n\n        return res // n\n\n    @staticmethod\n    def _calc_target_range(img, y, x, dy, dx):\n        assert dy is None or dy > 0\n        assert dx is None or dx > 0\n        dx = dx or 3\n        dy = dy or 3\n        left = x - dx if x - dx >= 0 else 0\n        right = x + dx + 1 if x + dx < img.width else img.width\n        top = y - dy if y - dy >= 0 else 0\n        bottom = y + dy + 1 if y + dy < img.height else img.height\n        width = right - left\n        height = bottom - top\n        return left, right, top, bottom, width, height\n\n    def _calc_contrast(self, img, y, x, dy, dx, rand):\n        assert 0 <= y < img.height and 0 <= x < img.width, \"Point out of image.\"\n        assert isinstance(rand, bool)\n\n        if rand:\n            return randint(0, 128) + 64 * ((x + y) % 2) + 64 * ((x + y) % 3 % 2)\n\n        l, r, t, b, w, h = self._calc_target_range(img, y, x, dy, dx)\n        n = w * h\n        sum_1 = sum_2 = 0\n        for y in range(t, b):\n            for x in range(l, r):\n                v = img.getpixel((x, y))\n                sum_1 += v\n                sum_2 += v * v\n        average = sum_1 / n\n\n        return sum_2 / n - average * average\n\n    def to_image(self, args, dither, dy, dx):\n        assert dx is None or dx > 0, 'dx must >= 0.'\n        assert dy is None or dy > 0, 'dy must >= 0.'\n\n        code_part_size = self._size - 2 * self._border\n\n        box_x, box_y = self._left + self._border, self._top + self._border\n        box = (box_x, box_y, box_x + code_part_size, box_y + code_part_size)\n        img = self._img.crop(box).resize((args.size, args.size))\n\n        if dither:\n            img = img.convert(\"1\")\n        else:\n            img = img.convert('L')\n            divider = self._calc_divider(img)\n            img = img.point(lambda v: 0 if v <= divider else 255, '1')\n\n        return img\n\n    def to_targets(self, canvas, args, dither, rand, dy, dx):\n        \"\"\"\n        :param QrCanvas canvas: canvas used to draw the QrCode.\n        :param QrArgs args: The args of QrCode.\n        :param bool dither: Make binary image with dithering or not.\n        :param bool rand: Make contrast of target random number.\n        :param int dy: Y offset when calc target.\n        :param int dx: X offset when calc target.\n        \"\"\"\n        temp = self.to_image(args, dither, dy, dx)\n\n        targets = [None] * args.cwc * 8\n        for y in range(temp.height):\n            for x in range(temp.width):\n                point = canvas.points[y][x]\n                if point.type in {QrPointType.DATA, QrPointType.CORRECTION}:\n                    fill = temp.getpixel((x, y)) == 0\n                    contrast = self._calc_contrast(temp, y, x, dy, dx, rand)\n                    targets[point.offset] = Target(y, x, fill, contrast, point)\n        return targets\n"
  },
  {
    "path": "pyqart/art/target.py",
    "content": "# Added at : 2016.8.3\n# Author   : 7sDream\n# Usage    : Target point(contain point and image pixel info).\n\n__all__ = ['Target']\n\n\nclass Target(object):\n    def __init__(self, y, x, fill, contrast, point):\n        self._y = y\n        self._x = x\n        self._fill = fill\n        self._contrast = contrast\n        self._point = point\n        self._hard_zero = False\n\n    @property\n    def fill(self):\n        return self._fill\n\n    @property\n    def contrast(self):\n        return self._contrast\n\n    @property\n    def y(self):\n        return self._y\n\n    @property\n    def x(self):\n        return self._x\n\n    @property\n    def point(self):\n        return self._point\n\n    def set_hard_zero(self):\n        self._hard_zero = True\n\n    def is_hard_zero(self):\n        return self._hard_zero\n\n    def __str__(self):\n        return \"Target({fill}, {contrast:.3f})\".format(\n            fill=self.fill, contrast=self.contrast\n        )\n"
  },
  {
    "path": "pyqart/common/__init__.py",
    "content": "from .bit_funcs import bit_at, set_bit, one_at, zero_at\nfrom .bits import Bits\nfrom .exception import InvalidTypeException\n\nBIT_PER_CW = BIT_PER_BYTE = 8\n"
  },
  {
    "path": "pyqart/common/bit_funcs.py",
    "content": "# Added at  : 2016.07.28\n# Author    : 7sDream\n# Usage     : Some common function to process data.\n\nimport functools\n\n\n__all__ = ['bit_at', 'one_at', 'zero_at', 'set_bit']\n\n\n@functools.lru_cache()\ndef one_at(pos, size=8):\n    \"\"\"\n    Create a size-bit int which only has one '1' bit at specific position.\n\n    example:\n\n    one_at(0) -> 0b10000000\n    one_at(3) -> 0b00010000\n    one_at(5, 10) -> 0b0000010000\n\n    :param int pos: Position of '1' bit.\n    :param int size: Length of value by bit.\n    :rtype: int\n    \"\"\"\n    assert 0 <= pos < size\n    return 1 << (size - 1 - pos)\n\n\n@functools.lru_cache()\ndef zero_at(pos, size=8):\n    \"\"\"\n    Create a size-bit int which only has one '0' bit at specific position.\n\n    :param int pos: Position of '0' bit.\n    :param int size: Length of value by bit.\n    :rtype: int\n    \"\"\"\n    assert 0 <= pos < size\n    return 2**size - 2**(size - pos - 1) - 1\n\n\ndef set_bit(value, pos, bit):\n    \"\"\"\n    Set bit at specific position of a 8-bit value to '1' or '0'\n\n    :param int value: Original value, 8 bit.\n    :param int pos: Position of bit which will be set.\n    :param bool bit: True for 1, False for 0.\n    :return: New value\n    :rtype: int\n    \"\"\"\n    assert 0 <= pos < 8\n\n    if bit:\n        return value | one_at(pos)\n    else:\n        return value & zero_at(pos)\n\n\ndef bit_at(value, length, pos):\n    \"\"\"\n    Get bit at pos of number, True for '1', False for '0':\n\n    :param int value: Int value to get the bit.\n    :param int length: Length of value by bit.\n    :param int pos: Bit position, highest position is 0.\n    :rtype: bool\n    \"\"\"\n\n    assert length > 0\n    assert 0 <= pos < length\n    if value == 0:\n        return False\n    return ((value >> (length - 1 - pos)) & one_at(7)) == 1\n"
  },
  {
    "path": "pyqart/common/bits.py",
    "content": "# Added at : 2016.7.28\n# Author   : 7sDream\n# Usage    : A utility class provide some bit level operation to bit stream.\n\nfrom .bit_funcs import set_bit, bit_at\n\n__all__ = ['Bits']\n\n_BIT_PER_BYTE = _BIT_PER_CW = 8\n\n_PADDING_BITS = 0b1110110000010001\n\"\"\"\npadding with those data when data not fill data codewords.\n\"\"\"\n\n\nclass Bits(object):\n    def __init__(self, value=None, length=None):\n        \"\"\"\n        Build a container that save value as a value_upper-bit size int.\n\n        :param int value: Value to init the container.\n        :value_upper int value_upper: Value value_upper by bit.\n        \"\"\"\n        self._length = 0\n        self._raw = bytearray(b'\\x00')\n        if value is not None:\n            length = value.bit_length() if length is None else length\n            self.append(value, length)\n\n    @classmethod\n    def copy_from(cls, other, start=0, count=None):\n        \"\"\"\n        Build object from other :any:`Bits`.\n\n        :param Bits|bytes|bytearray other: Target object.\n        :param int start: Where to start copy.\n        :param int count: How many data will be added.\n            Default is None, will add all data.\n        \"\"\"\n        obj = cls()\n        obj.extend(other, start, count)\n        return obj\n\n    @property\n    def length(self):\n        \"\"\"\n        :return: Data value_upper by bit.\n        :rtype: int\n        \"\"\"\n        return self._length\n\n    def __len__(self):\n        return self.length\n\n    @property\n    def capacity(self):\n        \"\"\"\n        :return: Capacity of object by bit.\n        :return: int\n        \"\"\"\n        return self.capacity_by_byte * _BIT_PER_BYTE\n\n    @property\n    def capacity_by_byte(self):\n        \"\"\"\n        :return: Capacity of object by byte.\n        :return: int\n        \"\"\"\n        return len(self._raw)\n\n    def append(self, value, length=None):\n        \"\"\"\n        Add a value_upper-bit int to container's end, use lower data of value.\n\n        :param int value: Value to be added.\n        :param int length: Length of value by bit,\n            default is None, which will use value.bit_length().\n        \"\"\"\n        if length is None:\n            length = value.bit_length()\n        for i in range(length):\n            self.append_bit(bit_at(value, length, i))\n\n    def append_bit(self, bit):\n        \"\"\"\n        Add one bit to container.\n\n        :param bool bit: True for 1, False for 0\n        \"\"\"\n        index = self.length\n        self._expand_capacity(self._length + 1)\n        self._length += 1\n        self[index] = bit\n\n    def extend(self, other, start=0, count=None):\n        \"\"\"\n        Add new value from other Bits.\n\n        :param Bits|bytes|bytearray other: Other data source want to be added.\n        :param int start: Where to start copy, default is 0.\n        :param int count: how many data will be extend.\n            default is None, will add all data.\n        :return: How many data be extended.\n        :rtype: int\n        \"\"\"\n        assert start >= 0\n        if isinstance(other, Bits):\n            end = len(other) if count is None else (start + count)\n            end = min(end, len(other))\n            for i in range(start, end):\n                self.append_bit(other[i])\n            return max(0, end - start)\n        elif isinstance(other, (bytes, bytearray)):\n            end = len(other) * _BIT_PER_BYTE \\\n                if count is None else (start + count)\n            end = min(end, len(other) * _BIT_PER_BYTE)\n            for i in range(start, end):\n                self.append_bit(bit_at(\n                    other[i // _BIT_PER_BYTE], _BIT_PER_BYTE,\n                    i % _BIT_PER_BYTE))\n            return max(0, end - start)\n        return 0\n\n    def xor(self, other, my_start=0, other_start=0, count=None):\n        \"\"\"\n        [001010001] xor [0010110],\n        self_start at 2 other_start at 3,\n        value_upper 3 will be [00<101>0001] xor [001<011>0] -> [00<110>0001]\n\n        :param Bit other: What to xor with.\n        :param int my_start: Where to start be xor.\n        :param int other_start: Where to start xor with.\n        :param int count: How many data will be xor,\n            default is None, will xor all possible.\n        :return: How many data be xor.\n        :rtype: int\n        \"\"\"\n        my_end = self.length if count is None else (my_start + count)\n        other_end = other.length if count is None else (other_start + count)\n        my_end = min(self.length, my_end)\n        other_end = min(other.length, other_end)\n        count = 0\n        for i, j in zip(range(my_start, my_end), range(other_start, other_end)):\n            self[i] = self[i] ^ other[j]\n            count += 1\n        return count\n\n    def pad(self, available, used):\n        # add terminator\n        self.append(0, min(available, 4))\n\n        # add more 0s to make last several data to a codeword\n        if self.length % _BIT_PER_CW != 0:\n            self.append(0, _BIT_PER_CW - self.length % _BIT_PER_CW)\n\n        # add pad bytes if the data is still not fill all data codewords\n        available = available + used - self.length\n        while available > 0:\n            self.append(_PADDING_BITS, min(available, 16))\n            available -= 16\n\n    def _expand_capacity(self, target):\n        \"\"\"\n        Expend capacity to ensure object can save \"target\" value_upper data.\n\n        :param int target: Target capacity by bit\n        \"\"\"\n        assert target >= 0\n        while target > self.capacity:\n            self._raw += b'\\x00' * self.capacity_by_byte\n\n    @property\n    def as_int(self):\n        \"\"\"\n        :return: View those data as int start at highest position,\n            -1 if no data.\n        :rtype: int\n        \"\"\"\n        if self.length == 0:\n            return -1\n        return int(self.as_string, 2)\n\n    @property\n    def as_string(self):\n        \"\"\"\n        :return: A string of \"01\" to represent those data.\n            empty string if no data.\n        :rtype: string\n        \"\"\"\n        return ''.join(['1' if x else '0' for x in self])\n\n    @property\n    def as_bytes(self):\n        return self._raw[:(self.length - 1) // _BIT_PER_BYTE + 1]\n\n    @property\n    def as_int_list(self):\n        return [int(x) for x in self.as_bytes]\n\n    def __str__(self):\n        s = self.as_string\n        return ', '.join([s[x:x + 8] for x in range(0, len(s), 8)])\n\n    def __repr__(self):\n        return \"Bits at {id}: [{self}]\".format(\n            id=id(self),\n            self=self,\n        )\n\n    def __iter__(self):\n        for i in range(self.length):\n            yield self[i]\n\n    def __getitem__(self, index):\n        if index >= self.length:\n            raise IndexError()\n        return bit_at(\n            self._raw[index // _BIT_PER_BYTE],\n            _BIT_PER_BYTE,\n            index % _BIT_PER_BYTE\n        )\n\n    def __setitem__(self, index, value):\n        if index >= self.length:\n            raise IndexError()\n        old_value = self._raw[index // _BIT_PER_BYTE]\n        new_value = set_bit(old_value, index % _BIT_PER_BYTE, value)\n        self._raw[index // _BIT_PER_BYTE] = new_value\n"
  },
  {
    "path": "pyqart/common/exception.py",
    "content": "# Added at  : 2016.07.30\n# Author    : 7sDream\n# Usage     : Exceptions raised by common functions or classes,\n\n\nclass InvalidTypeException(Exception):\n    def __init__(self, excepted, given, index, function):\n        self._excepted = excepted\n        self._given = given\n        self._function = function\n        self._index = index + 1\n\n    def __str__(self):\n        string = \"Invalid type at No.{number} argument of {function}, \" \\\n                 \"except {excepted}, {given} given.\"\n        return string.format(\n            index=self._index,\n            function=self._function,\n            excepted=self._excepted,\n            given=self._given,\n        )\n\n    __repr__ = __str__\n"
  },
  {
    "path": "pyqart/qart_entry.py",
    "content": "import argparse\nimport sys\nimport time\n\nfrom pyqart.art import QArtist\nfrom pyqart.qr.printer import QrImagePrinter, QrStringPrinter\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        prog=\"pyqart\",\n        description=\"A program of generate QArt Codes.\",\n        epilog=\"Writen by 7sDream. (https://github.com/7sDream/pyqart)\",\n    )\n    parser.add_argument(\n        'url', type=str,\n        help=\"url will be encode, like http://example.com/\",\n    )\n    parser.add_argument(\n        'img', type=str,\n        help=\"target image the QrCode will look like\",\n    )\n    parser.add_argument(\n        '-s', '--start-point', type=int, nargs=2,\n        help=\"left top point of the region of target image to use, \"\n             \"default is (0, 0).\",\n    )\n    parser.add_argument(\n        '-w', '--region-width', type=int,\n        help=\"target region width and height, \"\n             \"default will make region as bigger as possible\",\n    )\n    parser.add_argument(\n        '-d', '--dither', action=\"store_true\",\n        help=\"dithering when generate binary target image\",\n    )\n    parser.add_argument(\n        '-y', '--only-data', action=\"store_true\",\n        help=\"only use data bit points to approach the target image\",\n    )\n    parser.add_argument(\n        '-n', '--rand', action=\"store_true\",\n        help=\"generate point contrast by random, \"\n             \"if not provide, will use pixel nearby to calculate contrast\",\n    )\n    parser.add_argument(\n        '-f', '--higher-first', action='store_true',\n        help=\"pick pixel from higher contrast region first, \"\n             \"default will pick from lower region first\"\n    )\n    parser.add_argument(\n        '-x', '--yx', type=int, nargs=2,\n        help=\"yx region when calculate contrast\",\n    )\n    parser.add_argument(\n        '-v', '--version', type=int,\n        help=\"version of QrCode, 1 to 40, \"\n             \"will auto calculated from data length if not provide\",\n    )\n    parser.add_argument(\n        '-l', '--level', type=int, default=0,\n        help=\"QrCode error correction level, 0 to 3, default is 0\",\n    )\n    parser.add_argument(\n        '-m', '--mask', type=int,\n        help=\"mask of QrCode, 0 to 7, default is random value\",\n    )\n    parser.add_argument(\n        '-r', '--rotation', type=int, default=0,\n        help=\"rotate the QrCode(clockwise), \"\n             \"0 for no rotation, 1 for 90 degree, 2 for 180, 3 for 270\",\n    )\n    parser.add_argument(\n        '-p', '--point-size', type=int, default=3,\n        help=\"the point width and height of one QrCode point,\"\n             \" by pixel, default is 3\"\n    )\n    parser.add_argument(\n        '-b', '--board', type=int,\n        help=\"board wide by pixel, will auto calculated \"\n             \"from code size if not provide\",\n    )\n    parser.add_argument(\n        '-c', '--color', type=int, nargs=3, metavar=('R', 'G', 'B'),\n        help=\"front color of QrCode, 3 number as rgb color\",\n    )\n    parser.add_argument(\n        '-g', '--background-color', type=int, nargs=3, metavar=('R', 'G', 'B'),\n        help=\"background color of QrCode, 3 number as rgb color\",\n    )\n    parser.add_argument(\n        '-o', '--output', type=str,\n        help=\"output file path, code will print to terminal if not provide, \"\n             \"and other arguments will be ignored\"\n    )\n\n    argv = sys.argv[1:]\n\n    args = parser.parse_args(argv)\n\n    if args.yx is None:\n        args.yx = [None, None]\n    if args.color is not None:\n        args.color = tuple(args.color)\n    if args.background_color is not None:\n        args.background_color = tuple(args.background_color)\n\n    start = time.time()\n\n    artist = QArtist(args.url, args.img, args.version, args.mask, args.level,\n                     args.rotation, args.dither, args.only_data, args.rand,\n                     args.yx[0], args.yx[1])\n\n    if args.output is not None:\n        QrImagePrinter.print(\n            artist, args.output, args.point_size, args.board,\n            args.color, args.background_color\n        )\n        print('Done.')\n    else:\n        QrStringPrinter.print(artist, True)\n\n    end = time.time()\n    print(\"Used time:\", end-start, 'second.')\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "pyqart/qr/__init__.py",
    "content": "from .args import QrArgs\nfrom .data import (\n    QrData,\n    QrDataInvalidException,\n    QrEncodingException,\n    QrSpaceNotEnoughException\n)\nfrom .painter import QrPainter, QrCanvasException, QrPainterException\nfrom .printer import (\n    QrBasePrinter, QrImagePrinter, QrStringPrinter, QrHalftonePrinter\n)\nfrom .exception import QrException\n"
  },
  {
    "path": "pyqart/qr/args/__init__.py",
    "content": "from .args import QrArgs\n"
  },
  {
    "path": "pyqart/qr/args/args.py",
    "content": "# Added at : 2016.7.29\n# Author   : 7sDream\n# Usage    : All needed args to create a empty qr code.\n\nfrom .mask import QrMask\nfrom .version import QrVersion\nfrom .rotation import QrRotation\nfrom ..data import Raw, AlphaNumeric, Numbers\nfrom ...common import Bits\n\n__all__ = ['QrArgs']\n\n_FORMAT_POLY = Bits(0x537, 11)\n\"\"\"\n  x^10 + x^8 + x^5 + x^4 + x^2 + x + 1\n= 1    0  1 00 1     1   0 1    1   1\n= 0101 0011 0111\n= 0x573\n\nUsed when calculate format pattern data.\n\"\"\"\n\n_FORMAT_MASK_PATTERN = Bits(0x5412, 15)\n\"\"\"\nAfter all, format data should be xor with this mask pattern.\n\"\"\"\n\n_BC_ECCWCPB_TABLE = [\n    [(1, 7), (1, 10), (1, 13), (1, 17)],\n    [(1, 10), (1, 16), (1, 22), (1, 28)],\n    [(1, 15), (1, 26), (2, 18), (2, 22)],\n    [(1, 20), (2, 18), (2, 26), (4, 16)],\n    [(1, 26), (2, 24), (4, 18), (4, 22)],\n    [(2, 18), (4, 16), (4, 24), (4, 28)],\n    [(2, 20), (4, 18), (6, 18), (5, 26)],\n    [(2, 24), (4, 22), (6, 22), (6, 26)],\n    [(2, 30), (5, 22), (8, 20), (8, 24)],\n    [(4, 18), (5, 26), (8, 24), (8, 28)],\n    [(4, 20), (5, 30), (8, 28), (11, 24)],\n    [(4, 24), (8, 22), (10, 26), (11, 28)],\n    [(4, 26), (9, 22), (12, 24), (16, 22)],\n    [(4, 30), (9, 24), (16, 20), (16, 24)],\n    [(6, 22), (10, 24), (12, 30), (18, 24)],\n    [(6, 24), (10, 28), (17, 24), (16, 30)],\n    [(6, 28), (11, 28), (16, 28), (19, 28)],\n    [(6, 30), (13, 26), (18, 28), (21, 28)],\n    [(7, 28), (14, 26), (21, 26), (25, 26)],\n    [(8, 28), (16, 26), (20, 30), (25, 28)],\n    [(8, 28), (17, 26), (23, 28), (25, 30)],\n    [(9, 28), (17, 28), (23, 30), (34, 24)],\n    [(9, 30), (18, 28), (25, 30), (30, 30)],\n    [(10, 30), (20, 28), (27, 30), (32, 30)],\n    [(12, 26), (21, 28), (29, 30), (35, 30)],\n    [(12, 28), (23, 28), (34, 28), (37, 30)],\n    [(12, 30), (25, 28), (34, 30), (40, 30)],\n    [(13, 30), (26, 28), (35, 30), (42, 30)],\n    [(14, 30), (28, 28), (38, 30), (45, 30)],\n    [(15, 30), (29, 28), (40, 30), (48, 30)],\n    [(16, 30), (31, 28), (43, 30), (51, 30)],\n    [(17, 30), (33, 28), (45, 30), (54, 30)],\n    [(18, 30), (35, 28), (48, 30), (57, 30)],\n    [(19, 30), (37, 28), (51, 30), (60, 30)],\n    [(19, 30), (38, 28), (53, 30), (63, 30)],\n    [(20, 30), (40, 28), (56, 30), (66, 30)],\n    [(21, 30), (43, 28), (59, 30), (70, 30)],\n    [(22, 30), (45, 28), (62, 30), (74, 30)],\n    [(24, 30), (47, 28), (65, 30), (77, 30)],\n    [(25, 30), (49, 28), (68, 30), (81, 30)],\n]\n\"\"\"\nTable of (block count, error correction codeword count per block).\nRow by version, column by level.\n\"\"\"\n\n\nCCI_LENGTH_TABLE = {\n    Raw: [(9, 8), (40, 16)],\n    AlphaNumeric: [(9, 9), (26, 11), (40, 13)],\n    Numbers: [(9, 10), (26, 12), (40, 14)]\n}\n\"\"\"\nWhen data encoding, the Char Count Indicator value_upper table.\n\nif version <= first_item, cci_length is second_item.\n\"\"\"\n\n\nclass QrArgs(object):\n    def __init__(self, version, level=0, mask=0, rotation=0):\n        assert 0 <= level <= 3, \"Level must between 0 and 3.\"\n        self._version = QrVersion(version)\n        self._mask = QrMask(mask)\n        self._level = level\n        self._rotation = QrRotation(rotation)\n\n    @property\n    def size(self):\n        \"\"\"\n        :return: Width and height of QrCode.\n        :rtype: int\n        \"\"\"\n        return self._version.size\n\n    @property\n    def rotate_func(self):\n        return self._rotation.rotate_func\n\n    @property\n    def align_start(self):\n        \"\"\"\n        :return: See :any:`QrVersion.align_start`.\n        :rtype: int\n        \"\"\"\n        return self._version.align_start\n\n    @property\n    def align_step(self):\n        \"\"\"\n        :return: See :any:`QrVersion.align_step`.\n        :rtype: int\n        \"\"\"\n        return self._version.align_step\n\n    @property\n    def version_pattern_value(self):\n        \"\"\"\n        :return: See :any:`QrVersion.version_pattern_value`.\n        :rtype: int\n        \"\"\"\n        return self._version.version_pattern_value\n\n    @property\n    def version_number(self):\n        \"\"\"\n        :return: See :any:`QrVersion.number`.\n        :rtype: int\n        \"\"\"\n        return self._version.number\n\n    @property\n    def level(self):\n        \"\"\"\n        :return: See :any:`QrLevel.index`.\n        :rtype: int\n        \"\"\"\n        return self._level\n\n    @property\n    def mask_index(self):\n        \"\"\"\n        :return: See :any:`QrMask.index`.\n        :rtype: int\n        \"\"\"\n        return self._mask.index\n\n    @property\n    def format_pattern_bits(self):\n        \"\"\"\n        Format pattern has 15 bit,\n        split to 3 parts: level, mask, error correction.\n\n        It's structure like bellow:\n\n        14 13 12 11 10 9 8 7 6 5 4 3 2 1 0\n\n        Lv Lv M  M  M  C C C C C C C C C C\n\n        Level part value table：\n\n        ---- ----------- ---------\n        name index value bit value\n        ---- ----------- ---------\n         L        00         01\n         M        01         00\n         H        10         11\n         Q        11         10\n        ---- ----------- ---------\n\n        We can see: bit value = index value xor 01, 2 bit\n\n        QrMask from 0(000) to 7(111), 3 bit\n\n        Correction data is calculated by bch(15, 5), 10 bit\n\n        :return: The format pattern value in a :any:`Bits` object.\n        :rtype: Bits\n        \"\"\"\n        # level\n        bits = Bits(self.level ^ 0b01, 2)\n        # mask\n        bits.append(self.mask_index, 3)\n        # ec\n        bits.append(0, 10)\n        ec = Bits.copy_from(bits)\n        for i in range(5):\n            if ec[i]:\n                ec.xor(_FORMAT_POLY, i, 0)\n        for i in range(5, 15):\n            bits[i] = ec[i]\n        # masking\n        bits.xor(_FORMAT_MASK_PATTERN)\n        return bits\n\n    @property\n    def bc(self):\n        \"\"\"\n        :return: Block count.\n        :rtype: int\n        \"\"\"\n        return _BC_ECCWCPB_TABLE[self.version_number - 1][self.level][0]\n\n    @property\n    def eccwcpb(self):\n        \"\"\"\n        :return: Error Correction CodeWord Count Per Block.\n        :return: int\n        \"\"\"\n        return _BC_ECCWCPB_TABLE[self.version_number - 1][self.level][1]\n\n    @property\n    def cwc(self):\n        \"\"\"\n        :return: See :any:`QrVersion.cwc`.\n        :rtype: int\n        \"\"\"\n        return self._version.cwc\n\n    @property\n    def eccwc(self):\n        \"\"\"\n        :return: Error Correction CodeWord Count\n        :rtype: int\n        \"\"\"\n        return self.eccwcpb * self.bc\n\n    @property\n    def dcwc(self):\n        \"\"\"\n        :return: Data CodeWord Count.\n        :rtype: int\n        \"\"\"\n        return self.cwc - self.eccwc\n\n    @property\n    def ndcwcpb(self):\n        \"\"\"\n        :return: Normal Data CodeWord Count Per Block.\n        :rtype: int\n        \"\"\"\n        return self.dcwc // self.bc\n\n    @property\n    def edcwc(self):\n        \"\"\"\n        :return: Extra Data CodeWord Count.\n        :rtype: int\n        \"\"\"\n        return self.dcwc - self.ndcwcpb * self.bc\n\n    def dcwcof(self, index):\n        \"\"\"\n        :param int index: Block index.\n        :return: Data CodeWord Count OF No.index block.\n        :rtype: int\n        \"\"\"\n        assert index < self.bc\n        return self.ndcwcpb + (0 if index < (self.bc - self.edcwc) else 1)\n\n    @property\n    def should_invert(self):\n        \"\"\"\n        :return: See :any:`QrMask.should_invert`.\n        :rtype: callable\n        \"\"\"\n        return self._mask.should_invert\n\n    def cci_length_of(self, cls):\n        for sep, value in CCI_LENGTH_TABLE[cls]:\n            if self.version_number <= sep:\n                return value\n"
  },
  {
    "path": "pyqart/qr/args/mask.py",
    "content": "# Added at : 2016.7.29\n# Author   : 7sDream\n# Usage    : QrMask represent data part mask of QrCode,\n#            which changes which modules are dark and which are light\n#            according to a particular rule.\n#            The purpose of this step is to modify the QR code to make it\n#            as easy for a QR code reader to scan as possible.\n#            There are only 8 mask pattern can be used.\n\n__all__ = ['QrMask']\n\n_FUNCTION_LIST = [\n    lambda y, x: (x + y) % 2 == 0,\n    lambda y, x: y % 2 == 0,\n    lambda y, x: x % 3 == 0,\n    lambda y, x: (x + y) % 3 == 0,\n    lambda y, x: (y // 2 + x // 3) % 2 == 0,\n    lambda y, x: x * y % 2 + x * y % 3 == 0,\n    lambda y, x: (x * y % 2 + x * y % 3) % 2 == 0,\n    lambda y, x: ((x + y) % 2 + (x * y) % 3) % 2 == 0,\n]\n\"\"\"\nThe mask function table.\n\"\"\"\n\n\nclass QrMask(object):\n    def __init__(self, mask_index):\n        assert 0 <= mask_index <= 7, \"Mask must between 0 and 7\"\n        self._index = mask_index\n\n    @property\n    def index(self):\n        \"\"\"\n        :return: Mask index, from 0 to 7, specific the mask pattern.\n        :rtype: int\n        \"\"\"\n        return self._index\n\n    @property\n    def should_invert(self):\n        \"\"\"\n        :return: A function accept (y, x) to decide if point should invert.\n        :rtype: callable\n        \"\"\"\n        return _FUNCTION_LIST[self.index]\n"
  },
  {
    "path": "pyqart/qr/args/rotation.py",
    "content": "# Added at : 2016.7.29\n# Author   : 7sDream\n# Usage    : QrRotation represent the rotate of QrCode:\n#               0 for rotate 0 degrees clockwise, 1 for 90, 2 to 180, 3 for 270.\n\n__all__ = ['QrRotation']\n\n_ROTATE_FUNC_LIST = [\n    None,\n    lambda y, x, s: (x, s-y-1),\n    lambda y, x, s: (s-y-1, s-x-1),\n    lambda y, x, s: (s-x-1, y)\n]\n\n\nclass QrRotation(object):\n    def __init__(self, rotate_index):\n        assert 0 <= rotate_index <= 3, \"Rotation must between 0 and 3.\"\n        self._index = rotate_index\n\n    @property\n    def index(self):\n        return self._index\n\n    @property\n    def rotate_func(self):\n        return _ROTATE_FUNC_LIST[self.index]\n"
  },
  {
    "path": "pyqart/qr/args/version.py",
    "content": "# Added at : 2016.7.29\n# Author   : 7sDream\n# Usage    : QrVersion represent version of QrCode, which decide:\n#               code's size,\n#               value_upper codeword count,\n#               align pattern position,\n#               version pattern value.\n\n__all__ = ['QrVersion']\n\n_ALIGN_START_TABLE = [\n    100, 16, 20, 24, 28, 32, 20, 22, 24, 26,\n    28, 30, 32, 24, 24, 24, 28, 28, 28, 32,\n    26, 24, 28, 26, 30, 28, 32, 24, 28, 24,\n    28, 32, 28, 32, 28, 22, 26, 30, 24, 28,\n]\n\n_ALIGN_STEP_TABLE = [\n    100, 100, 100, 100, 100, 100, 16, 18, 20, 22,\n    24, 26, 28, 20, 22, 24, 24, 26, 28, 28,\n    22, 24, 24, 26, 26, 28, 28, 24, 24, 26,\n    26, 26, 28, 28, 24, 26, 26, 26, 28, 28,\n]\n\n_VERSION_PATTERN_VALUE_TABLE = [\n    0x0, 0x0, 0x0, 0x0, 0x0,\n    0x0, 0x7c94, 0x85bc, 0x9a99, 0xa4d3,\n    0xbbf6, 0xc762, 0xd847, 0xe60d, 0xf928,\n    0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6,\n    0x15683, 0x168c9, 0x177ec, 0x18ec4, 0x191e1,\n    0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75,\n    0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f,\n    0x24b0b, 0x2542e, 0x26a64, 0x27541, 0x28c69,\n]\n\n_CODEWORD_COUNT_TABLE = [\n    26, 44, 70, 100, 134, 172, 196, 242, 292, 346,\n    404, 466, 532, 581, 655, 733, 815, 901, 991, 1085,\n    1156, 1258, 1364, 1474, 1588, 1706, 1828, 1921, 2051, 2185,\n    2323, 2465, 2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706,\n]\n\n\nclass QrVersion(object):\n    def __init__(self, version_number):\n        assert 1 <= version_number <= 40, \"Version must between 1 and 40.\"\n        self._num = version_number\n\n    @property\n    def number(self):\n        \"\"\"\n        :return: The number represent of the version, from 1 to 40.\n        :rtype: int\n        \"\"\"\n        return self._num\n\n    @property\n    def size(self):\n        return 17 + 4 * self._num\n\n    @property\n    def align_start(self):\n        return _ALIGN_START_TABLE[self.number - 1]\n\n    @property\n    def align_step(self):\n        return _ALIGN_STEP_TABLE[self.number - 1]\n\n    @property\n    def version_pattern_value(self):\n        return _VERSION_PATTERN_VALUE_TABLE[self.number - 1]\n\n    @property\n    def cwc(self):\n        \"\"\"\n        CodeWord Count\n        \"\"\"\n        return _CODEWORD_COUNT_TABLE[self.number - 1]\n"
  },
  {
    "path": "pyqart/qr/data/__init__.py",
    "content": "from .data import QrData\nfrom .raw import Raw\nfrom .numbers import Numbers\nfrom .alphanumeric import AlphaNumeric\n\nfrom .exception import (\n    QrDataInvalidException,\n    QrEncodingException,\n    QrSpaceNotEnoughException,\n)\n"
  },
  {
    "path": "pyqart/qr/data/alphanumeric.py",
    "content": "# Added at : 2016.7.29\n# Author   : 7sDream\n# Usage    : Data mode that used to encode\n#            big alpha (A-Z)\n#            and numeric (0-9)\n#            and some special symbols (space, $, %, *, +, -, ., /, :).\n\nfrom .exception import QrDataInvalidException\nfrom .base import BaseType\nfrom ...common import Bits\n\n__all__ = ['AlphaNumeric']\n\n_ALPHA_NUMERIC_TABLE = {\n    ''.join([chr(c) for c in range(ord('0'), ord('9') + 1)]):\n        lambda c: int(c),\n    ''.join([chr(c) for c in range(ord('A'), ord('Z') + 1)]):\n        lambda c: ord(c) - ord('A') + 10,\n    ' $%*+-./:':\n        lambda c: ' $%*+-./:'.index(c) + 36\n}\n\n_DOMAIN = ''\nfor key in _ALPHA_NUMERIC_TABLE.keys():\n    _DOMAIN += key\n\n\nclass AlphaNumeric(BaseType):\n    def __init__(self, data, cci_length):\n        super().__init__(data, cci_length)\n\n    @property\n    def _mode_indicator(self):\n        return 0b0010\n\n    def _validate(self):\n        for i, v in enumerate(self.data):\n            if v not in _DOMAIN:\n                raise QrDataInvalidException(\n                    type(self).__name__,\n                    self.data,\n                    i,\n                )\n\n    @property\n    def _encoded_data_part(self):\n        bits = Bits()\n        for i in range(0, len(self.data), 2):\n            part = self.data[i:i + 2]\n            if len(part) == 2:\n                a, b = tuple(part)\n                length = 11\n            else:\n                a, b = part[0], None\n                length = 6\n            bits.append(self._calc_number(a, b), length)\n        return bits\n\n    @staticmethod\n    def _calc_number(a, b=None):\n        number1 = number2 = -1\n        for k, func in _ALPHA_NUMERIC_TABLE.items():\n            if a in k:\n                number1 = func(a)\n            if b is not None and b in k:\n                number2 = func(b)\n        if b is None:\n            return number1\n        else:\n            return number1 * 45 + number2\n\n    @property\n    def _encoded_data_part_length(self):\n        return 11 * (len(self.data) // 2) + 6 * (len(self.data) % 2)\n"
  },
  {
    "path": "pyqart/qr/data/base.py",
    "content": "# Added at : 2016.7.28\n# Author   : 7sDream\n# Usage    : A base data mode class for encoding data to bytes.\n#            All specific data model inherit from this class.\n\nimport abc\n\nfrom ...common import Bits\nfrom .exception import QrEncodingException\n\n__all__ = ['BaseType']\n\n\nclass BaseType(object):\n    def __init__(self, data, cci_length):\n        \"\"\"\n        :param data: Data to be encoded\n        :param int cci_length: value_upper of Char Count Indicator in bit\n        \"\"\"\n        assert len(data) > 0, 'Unable to encode empty data.'\n        self._data = data\n        self._cci_length = cci_length\n        self._validate()\n\n    @property\n    def data(self):\n        \"\"\"\n        :return: provided, raw original data\n        \"\"\"\n        return self._data\n\n    @property\n    @abc.abstractmethod\n    def _encoded_data_part_length(self):\n        return 0\n\n    @property\n    def needed_space(self):\n        return 4 + self._cci_length + self._encoded_data_part_length\n\n    @property\n    @abc.abstractmethod\n    def _mode_indicator(self):\n        \"\"\"\n        :return: A 4-bit data to indicate what model is using,\n            Use the lower 4 data.\n        :rtype: int\n        \"\"\"\n        pass\n\n    @property\n    def _char_count_indicator(self):\n        \"\"\"\n        :return: Placed before encoded data to indicate data value_upper,\n            it's own value_upper is decided by :any:`cci_length`.\n        :rtype: Bits\n        \"\"\"\n        bits = Bits()\n        bits.append(0, self._cci_length - len(self.data).bit_length())\n        bits.append(len(self.data), len(self.data).bit_length())\n        return bits\n\n    @abc.abstractmethod\n    def _validate(self):\n        \"\"\"\n        validate data, raise :any:`QrDataInvalidException`\n        if data is invalid, implemented by subclasses.\n\n        :raise: QrDataInvalidException\n        \"\"\"\n        pass\n\n    @property\n    @abc.abstractmethod\n    def _encoded_data_part(self):\n        \"\"\"\n        encode data to bytes use specific model, implemented by subclasses.\n\n        :return: encoded data\n        :rtype: Bits\n        \"\"\"\n        pass\n\n    @property\n    def output(self):\n        \"\"\"\n        :return: Output encoded data.\n        :rtype: Bits\n        \"\"\"\n        bits = Bits()\n        bits.append(self._mode_indicator, 4)\n        bits.extend(self._char_count_indicator)\n        bits.extend(self._encoded_data_part)\n        if bits.length != self.needed_space:\n            raise QrEncodingException(\n                type(self), self.data,\n                info=\"Encoded data value_upper does not match expectations.\",\n                exception=self.needed_space,\n                actual=bits.length,\n            )\n        return bits\n\n    def __str__(self):\n        mi = Bits()\n        mi.append(self._mode_indicator, 4)\n        cci = Bits()\n        cci.extend(self._char_count_indicator)\n        encoded_data = Bits()\n        encoded_data.extend(self._encoded_data_part)\n\n        string = \"{type} at {id:x}: \" \\\n                 \"{{data: {data}, mi: {mi}, cci: {cci}, encode: {code}}}\"\n        return string.format(\n            type=type(self).__name__, id=id(self),\n            data=self.data, mi=mi, cci=cci, code=encoded_data,\n        )\n"
  },
  {
    "path": "pyqart/qr/data/data.py",
    "content": "# Added at : 2016.7.30\n# Author   : 7sDream\n# Usage    : As a data set of (maybe) different data mode thad will be encoded.\n\nfrom .raw import Raw\nfrom .alphanumeric import AlphaNumeric\nfrom .numbers import Numbers\nfrom .exception import QrSpaceNotEnoughException\nfrom ...common import BIT_PER_CW\n\n\nclass QrData(object):\n    def __init__(self, string=None, ec_level=0):\n        from ..args import QrArgs\n        assert isinstance(string, str)\n        assert 0 <= ec_level <= 3\n        self._data_set = []\n        self._ec_level = ec_level\n        self._changed = False\n        self._last = (1, QrArgs(1).dcwc * BIT_PER_CW, 0)\n\n        if string is not None:\n            self.put_string(string)\n\n    @property\n    def size(self):\n        \"\"\"\n        :return: How many data item in object.\n        :rtype: int\n        \"\"\"\n        return len(self._data_set)\n\n    @property\n    def version_used_available(self):\n        from ..args import QrArgs\n        if self._changed is True:\n            args = None\n            used = 0\n            for i in range(1, 41):\n                args = QrArgs(i, self._ec_level)\n                encode_list = [cls(data, args.cci_length_of(cls))\n                               for cls, data in self._data_set]\n                used = sum([x.needed_space for x in encode_list])\n                available = args.dcwc * BIT_PER_CW - used\n                if available >= 0:\n                    self._last = (i, available, used)\n                    self._changed = False\n                    break\n            else:\n                raise QrSpaceNotEnoughException(\n                    args.dcwc * BIT_PER_CW, used\n                )\n        return self._last\n\n    @property\n    def ec_level(self):\n        return self._ec_level\n\n    def set_level(self, level):\n        assert 0 <= level <= 3\n        if self._ec_level != level:\n            self._ec_level = level\n            self._changed = True\n\n    def _common_put(self, data, cls):\n        if len(self._data_set) > 0 and self._data_set[-1][0] is cls:\n            old_data = self._data_set[-1][1]\n            self._data_set[-1] = (cls, old_data + data)\n        else:\n            self._data_set.append((cls, data))\n        self._changed = True\n\n    def put_string(self, string):\n        \"\"\"\n        Add string(utf-8) data to QrCode.\n\n        :param str string: The string will be added.\n        :return: A tuple: (if_success, exception).\n        :rtype: (bool, QrException)\n        :raise: QrDataInvalidException\n        \"\"\"\n        return self._common_put(string.encode('utf-8'), Raw)\n\n    def put_bytes(self, data):\n        \"\"\"\n        Add raw bytes data to QrCode.\n\n        :see-also:: :any:`put_string` for return and exception info.\n        \"\"\"\n        return self._common_put(data, Raw)\n\n    def put_numbers(self, numbers):\n        \"\"\"\n        Add numbers data to QrCode.\n\n        :see-also:: :any:`put_string` for return and exception info.\n\n        :param int|str numbers: The number will be added,\n            0 start at string type numbers will be preserved.\n        \"\"\"\n        return self._common_put(numbers, Numbers)\n\n    def put_alpha_numeric(self, string):\n        \"\"\"\n        Add numbers, big letters, and some special symbol data to QrCode.\n\n        :see-also:: :any:`put_string` for return and exception info.\n\n        :param str string: The data will be added.\n        \"\"\"\n        return self._common_put(string, AlphaNumeric)\n\n    @property\n    def data_set(self):\n        return tuple(self._data_set)\n"
  },
  {
    "path": "pyqart/qr/data/exception.py",
    "content": "# Added at : 2016.7.28\n# Author   : 7sDream\n# Usage    : Exception when encode data is not invalid.\n\nfrom ..exception import QrException\n\n__all__ = ['QrDataInvalidException', 'QrEncodingException',\n           'QrSpaceNotEnoughException']\n\n\nclass QrDataInvalidException(QrException):\n    def __init__(self, typename, invalid_data, index=None):\n        self.typename = typename\n        self.invalid_data = invalid_data\n        self.index = index\n\n    def __str__(self):\n        string = \"Invalid data \\\"{data}\\\" when build a {typename} data mode.\"\n        if self.index is not None:\n            string += \" first invalid position is {number}.\"\n        return string.format(\n            data=self.invalid_data,\n            typename=self.typename,\n            index=self.index,\n        )\n\n    __repr__ = __str__\n\n\nclass QrEncodingException(QrException):\n    def __init__(self, cls, data, **kwargs):\n        self._cls = cls\n        self._data = data\n        self._kwargs = kwargs\n\n    def __str__(self):\n        string = \"Error when encoding {cls}] type data [{data}].\"\n        if len(self._kwargs) > 0:\n            string += 'Additional information: ' + str(self._kwargs)\n\n\nclass QrSpaceNotEnoughException(QrException):\n    def __init__(self, available, need):\n        self._available = available\n        self._needed = need\n\n    def __str__(self):\n        string = \"There is not enough space to store the data provided, \"\n        string += \"{available} bit space available, data need {need} bit.\"\n        return string.format(\n            available=self._available,\n            need=self._needed\n        )\n\n    __repr__ = __str__\n"
  },
  {
    "path": "pyqart/qr/data/numbers.py",
    "content": "# Added at : 2016.7.28\n# Author   : 7sDream\n# Usage    : Numbers data model, 10 bit for 3 numbers.\n\nfrom .base import BaseType\nfrom ...common import Bits\nfrom .exception import QrDataInvalidException\n\n\nclass Numbers(BaseType):\n    def __init__(self, data, cci_length):\n        super().__init__(data, cci_length)\n\n    def _validate(self):\n        for i, value in enumerate(self.data):\n            if not ord('0') <= ord(value) <= ord('9'):\n                raise QrDataInvalidException(\n                    type(self).__name__, self.data, i)\n\n    @property\n    def _encoded_data_part(self):\n        bits = Bits()\n        split = (self.data[x:x + 3] for x in range(0, len(self.data), 3))\n        for i, string in enumerate(split):\n            bits.append(int(string), 1 + 3 * len(string))\n        return bits\n\n    @property\n    def _mode_indicator(self):\n        return 0b0001\n\n    @property\n    def _encoded_data_part_length(self):\n        return 10 * (len(self.data) // 3) + [0, 4, 7][len(self.data) % 3]\n"
  },
  {
    "path": "pyqart/qr/data/raw.py",
    "content": "# Added at : 2016.7.28\n# Author   : 7sDream\n# Usage    : Raw data model, 8 bit for a byte.\n\nfrom .base import BaseType\nfrom .exception import QrDataInvalidException\nfrom ...common import Bits\n\n__all__ = ['Raw']\n\n\nclass Raw(BaseType):\n    def __init__(self, data, cci_length):\n        super().__init__(data, cci_length)\n\n    @property\n    def _encoded_data_part(self):\n        bits = Bits()\n        bits.extend(self.data)\n        return bits\n\n    @property\n    def _mode_indicator(self):\n        return 0b0100\n\n    def _validate(self):\n        for i, value in enumerate(self.data):\n            if value < 0 or value > 255:\n                raise QrDataInvalidException(\n                    type(self).__name__,\n                    self.data,\n                    i,\n                )\n\n    @property\n    def _encoded_data_part_length(self):\n        return 8 * len(self.data)\n"
  },
  {
    "path": "pyqart/qr/ec/__init__.py",
    "content": "from .rsencoder import RSEncoder\n"
  },
  {
    "path": "pyqart/qr/ec/gf.py",
    "content": "# Added at : 2016.7.31\n# Author   : 7sDream\n# Usage    : Calculate GF(2^m) and all it's item a^n\n\nfrom ...common import bit_at\n\n__all__ = ['GF28']\n\n_MUL_CACHE = {}\n_ADD_CACHE = {}\n\n\nclass _GF2M(object):\n    def __init__(self, m, px):\n        self._m = m\n        self._px = px % self.value_upper\n        self._table, self._rev_table = self.calc()\n\n    @property\n    def value_upper(self):\n        return 2 ** self._m\n\n    @property\n    def index_upper(self):\n        return self.value_upper\n\n    def calc(self):\n        table = []\n        rev_table = [None] * self.value_upper\n        for x in range(self.index_upper):\n            if x < self._m:\n                value = 1 << x\n            elif x == self._m:\n                value = self._px\n            elif not bit_at(table[-1], self._m, 0):\n                value = (table[-1] << 1) % self.value_upper\n            else:\n                value = (table[-1] << 1 ^ self._px) % self.value_upper\n            table.append(value)\n            try:\n                rev_table[value] = x\n            except Exception as e:\n                print(value)\n                raise e\n        table.append(1)\n        return table, rev_table\n\n    def index(self, value):\n        return self._rev_table[value]\n\n    def __getitem__(self, index):\n        return _GFItem(self, index, self._table[index])\n\n\nclass _GFItem(object):\n    def __init__(self, gf, index, value):\n        self._gf = gf\n        self._index = index\n        self._value = value\n\n    @property\n    def gf(self):\n        return self._gf\n\n    @property\n    def index(self):\n        return self._index\n\n    @property\n    def value(self):\n        return self._value\n\n    def __add__(self, other):\n        cache_index = (self.gf, self.index, other.index)\n        if cache_index not in _ADD_CACHE:\n            value = self._value ^ other.value\n            if value == 0:\n                return None\n            item = _GFItem(self.gf, self.gf.index(value), value)\n            _ADD_CACHE[cache_index] = item\n        else:\n            item = _ADD_CACHE[cache_index]\n        return item\n\n    def __mul__(self, other):\n        cache_index = (self.gf, self.index, other.index)\n        if cache_index not in _MUL_CACHE:\n            index = self.index + other.index\n            index = index % self.gf.value_upper + int(index // self.gf.value_upper)\n            item = self.gf[index]\n            _MUL_CACHE[cache_index] = item\n        else:\n            item = _MUL_CACHE[cache_index]\n        return item\n\n    def __str__(self):\n        return \"a\" + str(self.index)\n\n\nGF28 = _GF2M(8, 0b100011101)\n"
  },
  {
    "path": "pyqart/qr/ec/poly.py",
    "content": "# Added at  : 2016.07.31\n# Author    : 7sDream\n# Usage     : Provide math operator with polynomials on GF28.\n#               Used in Reed-solomon encoder.\n\nimport abc\n\nfrom .gf import GF28\n\n__all__ = ['GF28Poly']\n\n\nclass _GFPoly(object):\n    @classmethod\n    @abc.abstractmethod\n    def gf(cls):\n        pass\n\n    def __init__(self, pcmap):\n        self._pcmap = pcmap\n        if self._pcmap:\n            self._max_index = max(self._pcmap.keys())\n        else:\n            self._max_index = 0\n\n    @classmethod\n    def from_index_list(cls, ilist, maxp):\n        pcmap = {}\n        for xi, ai in enumerate(ilist):\n            if ai is None:\n                continue\n            pcmap[maxp - xi] = cls.gf()[ai]\n        return cls(pcmap)\n\n    @classmethod\n    def from_value_list(cls, vlist, maxp):\n        pcmap = {}\n        for i, v in enumerate(vlist):\n            if v == 0:\n                continue\n            pcmap[maxp - i] = cls.gf()[cls.gf().index(v)]\n        return cls(pcmap)\n\n    @property\n    def pcmap(self):\n        return self._pcmap\n\n    @property\n    def max_index(self):\n        return self._max_index\n\n    @property\n    def as_int_list(self):\n        int_list = []\n        for p in reversed(range(self.max_index + 1)):\n            if p in self.pcmap:\n                int_list.append(self.pcmap[p].value)\n            else:\n                int_list.append(0)\n        return int_list\n\n    def __mul__(self, other):\n        new_pcmap = {}\n        for p1, c1 in self.pcmap.items():\n            for p2, c2 in other.pcmap.items():\n                if (p1 + p2) in new_pcmap:\n                    old_value = new_pcmap[p1 + p2]\n                    new_pcmap[p1 + p2] = old_value + c1 * c2\n                else:\n                    new_pcmap[p1 + p2] = c1 * c2\n        return type(self)(new_pcmap)\n\n    def __mod__(self, other):\n        r = type(self)(self.pcmap)\n        while r.max_index >= other.max_index:\n            pad = r.max_index - other.max_index\n            pad_item = type(self)({pad: r.pcmap[r.max_index]})\n            r += other * pad_item\n        return r\n\n    def __add__(self, other):\n        pcmap = {}\n        for p in range(max(self.max_index, other.max_index) + 1):\n            if p in self.pcmap:\n                pcmap[p] = self.pcmap[p]\n            if p in other.pcmap:\n                if p in pcmap:\n                    pcmap[p] += other.pcmap[p]\n                else:\n                    pcmap[p] = other.pcmap[p]\n                if pcmap[p] is None:\n                    del pcmap[p]\n        return type(self)(pcmap)\n\n    def __str__(self):\n        pc_list = sorted(self.pcmap.items(), key=lambda x: x[0], reverse=True)\n        strings = []\n        for p, c in pc_list:\n            if p == 0:\n                item = str(c)\n            elif p == 1:\n                item = str(c) + 'x'\n            else:\n                item = str(c) + 'x^' + str(p)\n            strings.append(item)\n        return '+'.join(strings)\n\n    def __repr__(self):\n        return \"Poly at {id}: {string}\".format(id=id(self), string=str(self))\n\n\nclass GF28Poly(_GFPoly):\n    @classmethod\n    def gf(cls):\n        return GF28\n"
  },
  {
    "path": "pyqart/qr/ec/rsencoder.py",
    "content": "from .poly import GF28Poly\nfrom ...common import Bits\n\n\nclass _RSGenPolynomials(object):\n    def __init__(self):\n        self._table = [None, GF28Poly.from_index_list([0, 0], 1)]\n\n    def __getitem__(self, index):\n        while index > len(self._table) - 1:\n            c = len(self._table) - 1\n            self._table.append(\n                self._table[-1] * GF28Poly.from_index_list([0, c], 1))\n        return self._table[index]\n\n\nRSGenPolynomials = _RSGenPolynomials()\n\n\nclass RSEncoder(object):\n    @classmethod\n    def encode(cls, data, ec_length, need_bits=True):\n        assert ec_length >= 0\n        if ec_length == 0:\n            return Bits()\n        if not isinstance(data, list):\n            data = data.as_int_list\n        data_length = len(data)\n        all_length = ec_length + data_length\n        m = GF28Poly.from_value_list(\n            data + [0] * ec_length,\n            all_length - 1\n        )\n        g = RSGenPolynomials[ec_length]\n        r = m % g\n        res = r.as_int_list\n        if len(res) < ec_length:\n            res = [0] * (ec_length - len(res)) + res\n        if need_bits:\n            return Bits.copy_from(bytearray(res))\n        else:\n            return res\n\n"
  },
  {
    "path": "pyqart/qr/exception.py",
    "content": "# Added at : 2016.7.29\n# Author   : 7sDream\n# Usage    : Base exception.\n\n__all__ = ['QrException']\n\n\nclass QrException(Exception):\n    pass\n"
  },
  {
    "path": "pyqart/qr/painter/__init__.py",
    "content": "from .painter import QrPainter\nfrom .exception import QrCanvasException, QrPainterException\n"
  },
  {
    "path": "pyqart/qr/painter/canvas.py",
    "content": "# Added at : 2016.7.29\n# Author   : 7sDream\n# Usage    : A canvas to draw the QrCode. It implements function to draw\n#            a basic QrCode whose data part is empty from QrArgs.\n\nfrom .exception import QrCanvasException\nfrom .point import QrPoint, QrPointType\nfrom ..args import QrArgs\n\n__all__ = ['QrCanvas']\n\n_TYPE_CHAR_MAP = {\n    QrPointType.UNKNOWN: '? ',\n    QrPointType.POSITION: 'Q*',\n    QrPointType.ALIGNMENT: 'A^',\n    QrPointType.TIMING: 'T-',\n    QrPointType.FORMAT: 'F<',\n    QrPointType.VERSION_PATTERN: 'V+',\n    QrPointType.UNUSED: 'Nu',\n    QrPointType.DATA: '@.',\n    QrPointType.CORRECTION: '#,',\n    QrPointType.EXTRA: 'E~',\n}\n\"\"\"\nThe char table use to convert QrCode to string to print.\n\"\"\"\n\n_BIT_PER_CW = 8\n\n\nclass QrCanvas(object):\n    def __init__(self, args):\n        assert isinstance(args, QrArgs), \"args argument must be QrArgs object.\"\n        self._args = args\n        self._size = self.args.size\n        self._points = [\n            [QrPoint(False) for _ in range(self.size)]\n            for _ in range(self.size)]\n\n        self._add_timing_pattern()\n        self._add_position_patterns()\n        self._add_align_patterns()\n        self._add_version_pattern()\n        self._add_unused_point()\n        self._add_format_pattern()\n        self._data_ec_points = self._add_empty_data_ec()\n        self._add_mask()\n        self._rotate()\n\n    def _add_timing_pattern(self):\n        \"\"\"\n        Add timing line to point array,  like bellow\n\n          0 1 2 3 4 5 6 7 8 9 ... -- x axis\n        0             @\n        1             .\n        2             @\n        3             .\n        4             @\n        5             .\n        6 @ . @ . @ . @ . @ . @ . @ . @ . @ . @ . @ . @ ....\n        7             .\n        8             @\n        9             .\n        10            @\n        11            .\n        12            @\n        13           ...\n        |\n        y axis\n\n        The (0-6, 6) and (6, 0-5) part will be override by position box.\n        \"\"\"\n        # rol 6 and col 6 is timing version_pattern_value\n        timing_position = 6\n        for i in range(self.size):\n            self._points[i][timing_position].type = QrPointType.TIMING\n            self._points[timing_position][i].type = QrPointType.TIMING\n            if i % 2 == 0:\n                self._points[i][timing_position].fill = True\n                self._points[timing_position][i].fill = True\n\n    def _add_position_patterns(self):\n        self._add_position_pattern(0, 0)\n        self._add_position_pattern(0, self.size - 7)\n        self._add_position_pattern(self.size - 7, 0)\n\n    def _add_align_patterns(self):\n        first_special_pos = 4\n        start = self.args.align_start\n        step = self.args.align_step\n        size = self.size\n        y = first_special_pos\n        while y + 5 < size:\n            x = first_special_pos\n            while x + 5 < size:\n                if self._check_align_box_position(y, x):\n                    self._add_align_pattern(y, x)\n                x = start if x == first_special_pos else (x + step)\n            y = start if y == first_special_pos else (y + step)\n\n    def _add_version_pattern(self):\n        version_block_pattern = self.args.version_pattern_value\n        if version_block_pattern != 0:\n            for x in range(6):\n                for y in range(3):\n                    point_a = self._points[self.size - 11 + y][x]\n                    point_b = self._points[x][self.size - 11 + y]\n                    point_a.type = point_b.type = QrPointType.VERSION_PATTERN\n                    if version_block_pattern & 1 != 0:\n                        point_a.fill = point_b.fill = True\n                    version_block_pattern >>= 1\n\n    def _add_unused_point(self):\n        point = self._points[self.size - 8][8]\n        point.type = QrPointType.UNUSED\n        point.fill = True\n\n    def _add_format_pattern(self):\n        for i, bit in enumerate(reversed(self.args.format_pattern_bits)):\n            # top left\n            if i < 6:\n                point_1 = self._points[i][8]\n            elif i < 8:\n                point_1 = self._points[i + 1][8]\n            elif i < 9:\n                point_1 = self._points[i][7]\n            else:\n                point_1 = self._points[8][14 - i]\n\n            # top right\n            if i < 8:\n                point_2 = self._points[8][self.size - 1 - i]\n            # bottom left\n            else:\n                point_2 = self._points[self.size - 15 + i][8]\n\n            point_1.type = point_2.type = QrPointType.FORMAT\n            point_1.fill = point_2.fill = bit\n\n    def _add_empty_data_ec(self):\n        # make data and extra data\n        dbc = self.args.dcwc * _BIT_PER_CW\n        ecbc = self.args.eccwc * _BIT_PER_CW\n        data_points = [QrPoint(False, QrPointType.DATA, offset)\n                       for offset in range(dbc)]\n        ec_points = [QrPoint(False, QrPointType.CORRECTION, dbc + offset)\n                     for offset in range(ecbc)]\n\n        # split into blocks\n        data_blocks = []\n        ec_blocks = []\n        ecbpb = self.args.eccwcpb * _BIT_PER_CW\n        di = eci = 0\n        for bi in range(self.args.bc):\n            dbcpb = self.args.dcwcof(bi) * _BIT_PER_CW\n            data_blocks.append(data_points[di:di + dbcpb])\n            ec_blocks.append(ec_points[eci:eci + ecbpb])\n            di += dbcpb\n            eci += ecbpb\n\n        if di != dbc or eci != ecbc:\n            raise QrCanvasException(\n                \"Error when split data and ec points to blocks.\")\n\n        # re-sort codewords\n        data_ec_points = []\n        for cwi in range(self.args.ndcwcpb + 1):  # cwi for CodeWord Index\n            for blki in range(self.args.bc):  # bi for BLocK Index\n                if cwi * _BIT_PER_CW < len(data_blocks[blki]):\n                    bi = cwi * _BIT_PER_CW\n                    data_ec_points.extend(\n                        data_blocks[blki][bi:bi + _BIT_PER_CW])\n        for cwi in range(self.args.eccwcpb):\n            for blki in range(self.args.bc):\n                bi = cwi * _BIT_PER_CW\n                data_ec_points.extend(ec_blocks[blki][bi:bi + _BIT_PER_CW])\n\n        if len(data_ec_points) != dbc + ecbc:\n            raise QrCanvasException(\"Error when resort codewords.\")\n\n        # add remain points\n        # value_upper remaining data count is value_upper(0, 3, 4, 7) = 7\n        for i in range(7):\n            data_ec_points.append(QrPoint(False, QrPointType.EXTRA))\n\n        # re place points to canvas\n        ai = 0  # for All Index\n        x = self.size\n\n        def place_two_column(reverse, now_index):\n            y_list = range(self.size)\n            for y in (y_list if not reverse else reversed(y_list)):\n                if self._points[y][x - 1].type is QrPointType.UNKNOWN:\n                    self._points[y][x - 1] = data_ec_points[now_index]\n                    now_index += 1\n                if self._points[y][x - 2].type is QrPointType.UNKNOWN:\n                    self._points[y][x - 2] = data_ec_points[now_index]\n                    now_index += 1\n            return now_index\n\n        while x > 0:\n            ai = place_two_column(True, ai)\n            x -= 2\n            x = 6 if x == 7 else x\n            ai = place_two_column(False, ai)\n            x -= 2\n\n        return data_ec_points\n\n    def _add_mask(self):\n        for y in range(self.size):\n            for x in range(self.size):\n                point = self._points[y][x]\n                if point.type in {QrPointType.DATA, QrPointType.CORRECTION,\n                                  QrPointType.EXTRA}:\n                    point.invert = self.args.should_invert(y, x)\n\n    def _add_position_pattern(self, y, x):\n        \"\"\"\n        add big position box to pixels array, box pattern like bellow\n\n           -1 0 1 2 3 4 5 6 7 -- x(i) axis offset\n        -1  . . . . . . . . .\n         0  . # @ @ @ @ @ @ .\n         1  . @ . . . . . @ .\n         2  . @ . @ @ @ . @ .\n         3  . @ . @ @ @ . @ .\n         4  . @ . @ @ @ . @ .\n         5  . @ . . . . . @ .\n         6  . @ @ @ @ @ @ @ .\n         7  . . . . . . . . .\n         |\n        y(j)\n        axis\n        offset\n\n            . for white pixel\n            @ for black pixel\n            # start pixel\n\n        :param x: left of start pixel\n        :param y: top of start pixel\n        \"\"\"\n\n        # ===== generate inside 7 x 7 box =====\n        for i in range(7):\n            for j in range(7):\n                #  left, right     up, bottom           fill inside rect\n                point = self._points[x + i][y + j]\n                point.type = QrPointType.POSITION\n                if i in {0, 6} or j in {0, 6} or (2 <= i <= 4 and 2 <= j <= 4):\n                    point.fill = True\n\n        # ===== generate left and right white border =====\n\n        for j in range(-1, 8):\n            if self._check_index(y + j):\n                if self._check_index(x - 1):\n                    # left\n                    self._points[y + j][x - 1].type = QrPointType.POSITION\n                if self._check_index(x + 7):\n                    # right\n                    self._points[y + j][x + 7].type = QrPointType.POSITION\n\n        for i in range(-1, 8):\n            if self._check_index(x + i):\n                if self._check_index(y - 1):\n                    # top\n                    self._points[y - 1][x + i].type = QrPointType.POSITION\n                if self._check_index(y + 7):\n                    # bottom\n                    self._points[y + 7][x + i].type = QrPointType.POSITION\n\n    def _add_align_pattern(self, y, x):\n        \"\"\"\n        add align box to pixels array, version_pattern_value like bellow\n\n          0 1 2 3 4 -- x(i) axis offset\n        0 # @ @ @ @\n        1 @ . . . @\n        2 @ . @ . @\n        3 @ . . . @\n        4 @ @ @ @ @\n        |\n        y(j) axis offset\n\n        :param x: left of start pixel\n        :param y: top of start pixel\n        \"\"\"\n        for j in range(5):\n            for i in range(5):\n                point = self._points[y + j][x + i]\n                point.type = QrPointType.ALIGNMENT\n                if i in {0, 4} or j in {0, 4} or i == j == 2:\n                    point.fill = True\n\n    def _check_index(self, x):\n        return 0 <= x < self.size\n\n    def _check_align_box_position(self, y, x):\n        return QrPointType.POSITION not in {\n            self._points[y][x].type,\n            self._points[y][x + 5].type,\n            self._points[y + 5][x].type,\n            self._points[y + 5][x + 5].type\n        }\n\n    def _rotate(self):\n        if self.args.rotate_func is None:\n            return\n        new = [[None for _ in range(self.size)] for __ in range(self.size)]\n        for y in range(self.size):\n            for x in range(self.size):\n                new_y, new_x = self.args.rotate_func(y, x, self.size)\n                new[new_y][new_x] = self.points[y][x]\n        self._points = new\n\n    @property\n    def args(self):\n        return self._args\n\n    @property\n    def size(self):\n        return self._size\n\n    @property\n    def data_ec_points(self):\n        return self._data_ec_points\n\n    def load_data(self, bits):\n        data_ec_length = len(self._data_ec_points) - 7\n        assert bits.length == data_ec_length\n        for _, point in zip(range(data_ec_length), self._data_ec_points):\n            point.fill = bits[point.offset]\n\n    @property\n    def points(self):\n        return self._points\n\n    def __str__(self):\n        lines = []\n        for row in self._points:\n            line = []\n            for point in row:\n                fill = point.fill if not point.invert else not point.fill\n                line.append(_TYPE_CHAR_MAP[point.type][0 if fill else 1])\n            lines.append(' '.join(line))\n        return '\\n'.join(lines)\n"
  },
  {
    "path": "pyqart/qr/painter/exception.py",
    "content": "# Added at : 2016.7.29\n# Author   : 7sDream\n# Usage    : Exception raised when drawing or add data to QrCode.\n\nfrom ..exception import QrException\n\n__all__ = ['QrCanvasException', 'QrPainterException']\n\n\nclass QrCanvasException(QrException):\n    pass\n\n\nclass QrPainterException(QrException):\n    pass\n"
  },
  {
    "path": "pyqart/qr/painter/painter.py",
    "content": "# Added at : 2016.7.30\n# Author   : 7sDream\n# Usage    : A painter that draw paint data to canvas.\n\nfrom random import randint\n\nfrom .canvas import QrCanvas\nfrom .exception import QrPainterException\nfrom ..args import QrArgs\nfrom ..data import QrData\nfrom ..ec import RSEncoder\nfrom ...common import Bits, BIT_PER_CW\n\n__all__ = ['QrPainter']\n\n\nclass QrPainter(object):\n    def __init__(self, data, version=None, mask=None, rotation=0):\n        \"\"\"\n        :param QrData|str data: Data will be paint to QrCode.\n        :param int version: Version of QrCode, 1 to 40,\n            None means use the minimum version can encode the data you provided.\n        :param int mask: The mask used in data and ec parts, from 0 to 7,\n            None for random pick.\n        :param int rotation: QrCode rotation direction, 0 for no rotation,\n            1 for 90 degree clockwise, 2 for 180, 3 for 270.\n        \"\"\"\n        assert isinstance(data, (QrData, str)), \"Data must be str or QrData.\"\n        if isinstance(data, str):\n            data = QrData(data)\n        if mask is None:\n            mask = randint(0, 7)\n        self._data = data\n        self._version = version\n        self._mask = mask\n        self._rotation = rotation\n        self._get_and_test_params()\n\n    def _get_and_test_params(self):\n        min_version, available, used = self._data.version_used_available\n        if self._version is not None and self._version < min_version:\n            raise QrPainterException(\n                \"The {} version QrCode does not have enough space to encode \"\n                \"the data your provided, minimum version is {}.\".format(\n                    self._version, min_version,\n                ))\n        return min_version, available, used\n\n    def get_params(self):\n        min_version, available, used = self._get_and_test_params()\n        if self._version is None:\n            version = min_version\n        else:\n            version = self._version\n            args = QrArgs(version, self._data.ec_level)\n            encode_list = [cls(data, args.cci_length_of(cls))\n                           for cls, data in self._data.data_set]\n            used = sum([x.needed_space for x in encode_list])\n            available = args.dcwc * BIT_PER_CW - used\n        return QrArgs(version, self._data.ec_level, self._mask,\n                      self._rotation), available, used\n\n    @property\n    def data_bits(self):\n        if self._data.size == 0:\n            raise QrPainterException(\"Unable to paint EMPTY DATA to canvas.\")\n\n        args, available, used = self.get_params()\n\n        bits = Bits()\n\n        # add encoded data\n        for cls, data in self._data.data_set:\n            encoding = cls(data, args.cci_length_of(cls))\n            bits.extend(encoding.output)\n\n        # ensure encoding process as expect\n        assert bits.length == used\n\n        bits.pad(available, used)\n\n        # ensure fill all data zone\n        assert bits.length == args.dcwc * BIT_PER_CW\n\n        return bits\n\n    @property\n    def bits(self):\n        \"\"\"\n        :rtype: Bits\n        :raise: QrEncodingException: When encoding process error.\n        \"\"\"\n        bits = self.data_bits\n        args, _, _ = self.get_params()\n        ec_bits = Bits()\n        ec_length = args.eccwcpb\n        di = 0\n        for bi in range(args.bc):\n            dcwc = args.dcwcof(bi)\n            dbcob = dcwc * BIT_PER_CW\n            block_data_bits = Bits.copy_from(bits, di, dbcob)\n            ec_bits.extend(RSEncoder.encode(block_data_bits, ec_length))\n            di += dbcob\n        bits.extend(ec_bits)\n        return bits\n\n    @property\n    def canvas(self):\n        args, _, _ = self.get_params()\n        canvas = QrCanvas(args)\n        return canvas\n\n    @property\n    def as_bool_matrix(self):\n        res = []\n        canvas = self.canvas\n        canvas.load_data(self.bits)\n        for row in canvas.points:\n            line = []\n            for point in row:\n                line.append(point.fill if not point.invert else not point.fill)\n            res.append(line)\n        return res\n"
  },
  {
    "path": "pyqart/qr/painter/point.py",
    "content": "# Added at : 2016.7.29\n# Author   : 7sDream\n# Usage    : QrPoint represent a point of QrCode, can be 9 type(except UNKNOWN).\n#            Enum class QrPointType used to stand for those type.\n#            The UNKNOWN type only be used to init the point which means\n#            the type of point is temporarily unknown.\n\nfrom enum import Enum, unique\n\n__all__ = ['QrPointType', 'QrPoint']\n\n\n@unique\nclass QrPointType(Enum):\n    UNKNOWN = 0\n    POSITION = 1\n    ALIGNMENT = 2\n    TIMING = 3\n    FORMAT = 4\n    VERSION_PATTERN = 5\n    UNUSED = 6\n    DATA = 7\n    CORRECTION = 8\n    EXTRA = 9\n\n\nclass QrPoint(object):\n    def __init__(self, fill, type_=QrPointType.UNKNOWN, offset=-1,\n                 invert=False):\n        self.fill = fill\n        self.type = type_\n        self.offset = offset\n        self.invert = invert\n"
  },
  {
    "path": "pyqart/qr/printer/__init__.py",
    "content": "from .base import QrBasePrinter\nfrom .image_printer import QrImagePrinter\nfrom .string_printer import QrStringPrinter\nfrom .halftone_printer import QrHalftonePrinter\n\n"
  },
  {
    "path": "pyqart/qr/printer/base.py",
    "content": "# Added at : 2016.7.31\n# Author   : 7sDream\n# Usage    : Base printer to visualize QrCode, define the function should be\n#            implement by subclasses.\n\nimport abc\n\nfrom ..painter import QrPainter\nfrom ..data import QrData\n\n\nclass QrBasePrinter(object):\n    def __init__(self):\n        pass\n\n    @classmethod\n    def _create_painter(cls, obj):\n        assert isinstance(\n            obj,\n            (QrPainter, QrData, str, bytes, bytearray)\n        ), \"Argument must be QrPainter, QrData, str, bytes or bytearray\"\n        if isinstance(obj, QrData):\n            obj = QrPainter(obj)\n        elif isinstance(obj, str):\n            obj = QrPainter(QrData(obj))\n        elif isinstance(obj, (bytes, bytearray)):\n            data = QrData()\n            obj = data.put_bytes(obj)\n            obj = QrPainter(obj)\n        return obj\n\n    @abc.abstractmethod\n    def print(self, *args, **kwargs):\n        pass\n"
  },
  {
    "path": "pyqart/qr/printer/halftone_printer.py",
    "content": "from __future__ import division\n\nimport PIL.ImageDraw as Draw\nimport PIL.Image as Image\n\nfrom .image_printer import QrImagePrinter\nfrom ..painter.point import QrPointType\nfrom ...art.qart import QArtist\n\n\nclass QrHalftonePrinter(QrImagePrinter):\n\n    @classmethod\n    def print(cls, obj, path=None, point_width=None, border_width=None,\n              f_color=None, bg_color=None, file_format=None,\n              img=None, colorful=True, pixelization=False):\n\n        super_class = super(QrHalftonePrinter, cls)\n\n        point_width, border_width = super_class._calc_point_border_width(\n            point_width, border_width)\n\n        if not colorful:\n            bg_color = (255, 255, 255)\n            f_color = (0, 0, 0)\n\n        point_width = int(((point_width - 1) // 3 + 1) * 3)\n        mask_block_width = point_width // 3\n        painter = cls._create_painter(obj)\n        canvas = painter.canvas\n\n        if isinstance(painter, QArtist) and img is None:\n            img = painter.source.to_image(\n                canvas.args, painter.dither, painter.dy, painter.dx\n            )\n\n        pass_path = path if img is None else None\n\n        qr = super_class.print(\n            painter, pass_path, point_width, border_width,\n            f_color, bg_color, file_format,\n        )\n\n        qr_size = qr.size[0] - 2 * border_width\n\n        if img is not None:\n            if not isinstance(painter, QArtist):\n                if not isinstance(img, Image.Image):\n                    img = Image.open(str(img))\n                if not colorful:\n                    if pixelization:\n                        img = img.convert('L')\n                    else:\n                        img = img.convert('1')\n                if pixelization:\n                    img = img.resize((canvas.size, canvas.size))\n        else:\n            return qr\n\n        img = img.resize((qr_size, qr_size))\n\n        x = y = 0\n        mask = Image.new('1', (qr_size, qr_size), 0)\n        drawer = Draw.Draw(mask, '1')\n        for line in canvas.points:\n            for point in line:\n                if point.type in {QrPointType.DATA, QrPointType.CORRECTION}:\n                    drawer.rectangle(\n                        [x, y, x + point_width - 1, y + point_width - 1],\n                        fill=1, outline=1,\n                    )\n                    drawer.rectangle(\n                        [\n                            x + mask_block_width,\n                            y + mask_block_width,\n                            x + 2 * mask_block_width - 1,\n                            y + 2 * mask_block_width - 1,\n                        ],\n                        fill=0, outline=0\n                    )\n                x += point_width\n            x, y = 0, y + point_width\n\n        # uncomment the following code to see mask image\n        # mask.save(path + '.mask.bmp', format='bmp')\n\n        qr.paste(img, (border_width, border_width), mask)\n\n        if path is not None:\n            qr.save(path, format=file_format)\n            return None\n\n        return qr\n"
  },
  {
    "path": "pyqart/qr/printer/image_printer.py",
    "content": "# Added at : 2016.7.31\n# Author   : 7sDream\n# Usage    : A printer that print QrCode to a image.\n\nfrom .base import QrBasePrinter\nfrom ..data import QrData\n\nimport PIL.Image as Image\nimport PIL.ImageDraw as Draw\n\n\nclass QrImagePrinter(QrBasePrinter):\n\n    @classmethod\n    def _calc_point_border_width(cls, point_width, border_width):\n        point_width = int(point_width) if point_width is not None else 1\n        border_width = point_width if border_width is None else border_width\n        border_width = max(1, border_width)\n        return point_width, border_width\n\n    @classmethod\n    def print(cls, obj, path=None, point_width=None, border_width=None,\n              f_color=None, bg_color=None, file_format=None):\n        \"\"\"\n        Print the QrCode to a image.\n\n        :param QrPainter|QrData|str|bytes|bytearray obj:\n            The painter that want print his/her QrCode,\n            or a raw QrData object which contains data,\n            or just a string or bytes(bytearray) which will used as data.\n        :param str path: If provided, will auto save file to the path.\n        :param int point_width: Width and Height of code part.\n            None will be 1 pixel per point.\n        :param border_width: Border width, None will be code width / 20.\n        :param (int, int, int) f_color: Front color, Default is black.\n        :param (int, int, int) bg_color: Background color, Default is white.\n        :param str file_format: Image suffix, like png, jpeg, bmp, etc.\n        :return: Bytes data of image **Only when file path is not provided**.\n        :rtype: PIL.Image\n        \"\"\"\n\n        point_width, border_width = cls._calc_point_border_width(\n            point_width, border_width)\n\n        obj = cls._create_painter(obj)\n\n        matrix = obj.as_bool_matrix\n        size = len(matrix)\n        code_width = size * point_width\n        img_size = code_width + 2 * border_width\n\n        f_color = (0, 0, 0) if f_color is None else f_color\n        bg_color = (255, 255, 255) if bg_color is None else bg_color\n\n        qr_img = Image.new('RGB', (size, size), bg_color)\n        drawer = Draw.Draw(qr_img)\n\n        fill_points = []\n        for y in range(size):\n            for x in range(size):\n                if matrix[y][x]:\n                    fill_points.append((x, y))\n\n        drawer.point(fill_points, f_color)\n        del drawer\n\n        qr_img = qr_img.resize((code_width, code_width))\n\n        img = Image.new('RGB', (img_size, img_size), bg_color)\n        img.paste(qr_img, (border_width, border_width))\n\n        if path is not None:\n            img.save(path, format=file_format)\n            return None\n\n        return img\n"
  },
  {
    "path": "pyqart/qr/printer/string_printer.py",
    "content": "# Added at : 2016.8.1\n# Author   : 7sDream\n# Usage    : A printer that print QrCode to a string, can be show in shell.\n\nfrom .base import QrBasePrinter\n\nWHITE_ALL = '\\u2588'\nWHITE_BLACK = '\\u2580'\nBLACK_WHITE = '\\u2584'\nBLACK_ALL = ' '\n\nMAP = {\n    (True, True): BLACK_ALL,\n    (True, False): BLACK_WHITE,\n    (False, True): WHITE_BLACK,\n    (False, False): WHITE_ALL,\n}\n\n\nclass QrStringPrinter(QrBasePrinter):\n    @classmethod\n    def print(cls, obj, print_out=True, *args, **kwargs):\n        \"\"\"\n        :param obj: See :any:`QrImagePrinter`\n        :param bool print_out: Whether to print QrCode out.\n        :return: The string that can be print out like a QrCode.\n        :type: string\n        \"\"\"\n        painter = cls._create_painter(obj)\n        matrix = painter.as_bool_matrix\n        matrix = [[False] + x + [False] for x in matrix]\n        size = len(matrix) + 2\n        matrix.insert(0, [False] * size)\n        matrix.append([False] * size)\n        matrix.append([True] * size)\n        lines = []\n        for row in range(0, size, 2):\n            line = []\n            for col in range(0, size):\n                line.append(MAP[(matrix[row][col], matrix[row + 1][col])])\n            lines.append(''.join(line))\n        string = '\\n'.join(lines)\n        if print_out:\n            print(string)\n        return string\n"
  },
  {
    "path": "pyqart/qr_entry.py",
    "content": "import argparse\nimport sys\n\nfrom pyqart.qr.data import QrData\nfrom pyqart.qr.painter import QrPainter\nfrom pyqart.qr.printer import QrImagePrinter, QrStringPrinter\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        prog=\"pyqr\",\n        description=\"A program of generate QrCode.\",\n        epilog=\"Writen by 7sDream. (https://github.com/7sDream/pyqart)\",\n    )\n    parser.add_argument(\n        'string', type=str,\n        help=\"string will be encode\"\n    )\n    parser.add_argument(\n        '-v', '--version', type=int,\n        help=\"version of QrCode, 1 to 40, \"\n             \"will auto calculated from data length if not provide\"\n    )\n    parser.add_argument(\n        '-l', '--level', type=int, default=0,\n        help=\"QrCode error correction level, 0 to 3, default is 0\"\n    )\n    parser.add_argument(\n        '-m', '--mask', type=int,\n        help=\"mask of QrCode, 0 to 7, default is random value\"\n    )\n    parser.add_argument(\n        '-r', '--rotation', type=int, default=0,\n        help=\"rotate the QrCode(clockwise), \"\n             \"0 for no rotation, 1 for 90 degree, 2 for 180, 3 for 270\"\n    )\n    parser.add_argument(\n        '-p', '--point-size', type=int, default=3,\n        help=\"the point width and height of one QrCode point,\"\n             \" by pixel, default is 3\"\n    )\n    parser.add_argument(\n        '-b', '--board', type=int,\n        help=\"board wide by pixel, will auto calculated \"\n             \"from code size if not provide\",\n    )\n    parser.add_argument(\n        '-c', '--color', type=int, nargs=3, metavar=('R', 'G', 'B'),\n        help=\"front color of QrCode, 3 number as rgb color\",\n    )\n    parser.add_argument(\n        '-g', '--background-color', type=int, nargs=3, metavar=('R', 'G', 'B'),\n        help=\"background color of QrCode, 3 number as rgb color\",\n    )\n    parser.add_argument(\n        '-o', '--output', type=str,\n        help=\"output file path, code will print to terminal if not provide, \"\n             \"and other arguments will be ignored\"\n    )\n\n    argv = sys.argv[1:]\n\n    args = parser.parse_args(argv)\n\n    data = QrData(args.string, args.level)\n    painter = QrPainter(data, args.version, args.mask, args.rotation)\n\n    if args.color is not None:\n        args.color = tuple(args.color)\n    if args.background_color is not None:\n        args.background_color = tuple(args.background_color)\n\n    if args.output is not None:\n        QrImagePrinter.print(\n            painter, args.output, args.point_size, args.board,\n            args.color, args.background_color\n        )\n    else:\n        QrStringPrinter.print(painter, True)\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nfrom setuptools import setup, find_packages\n\nimport pyqart\n\npackages = find_packages(exclude=[\"*.tests\", \"*.tests.*\", \"tests.*\", \"tests\"])\n\nsetup(\n    name='pyqart',\n    keywords=['qrcode', 'qart'],\n    version=pyqart.__version__,\n    description='QArt Python implementation, '\n                'see http://research.swtch.com/qart for details.',\n    author='7sDream',\n    author_email='7seconddream@gmail.com',\n    license='MIT',\n    url='https://github.com/7sDream/pyqart',\n\n    install_requires=['pillow'],\n    packages=packages,\n\n    classifiers=[\n        'Development Status :: 3 - Alpha',\n        'Environment :: Console',\n        'Intended Audience :: Developers',\n        'Intended Audience :: End Users/Desktop',\n        'License :: OSI Approved :: MIT License',\n        'Natural Language :: English',\n        'Operating System :: OS Independent',\n        'Programming Language :: Python :: 3 :: Only',\n        'Topic :: Multimedia :: Graphics',\n        'Topic :: Software Development :: Libraries :: Python Modules',\n    ],\n\n    entry_points={\n        'console_scripts': [\n            'pyqr = pyqart.qr_entry:main',\n            'pyqart = pyqart.qart_entry:main',\n        ]\n    }\n)\n"
  },
  {
    "path": "test/__init__.py",
    "content": ""
  },
  {
    "path": "test/test_alphanumeric.py",
    "content": "import unittest\n\nfrom pyqart.qr.data.alphanumeric import AlphaNumeric\n\n\nclass TestAlphaNumeric(unittest.TestCase):\n    def test_alphanumeric_odd(self):\n        an = AlphaNumeric('AC-42', 9)\n        self.assertEqual(\n            an.output.as_string,\n            '0010' + '000000101' +\n            '00111001110' + '11100111001' + '000010'\n        )\n\n    def test_alphanumeric_even(self):\n        an = AlphaNumeric('7S DREAM', 9)\n        self.assertEqual(\n            an.output.as_string,\n            '0010' + '000001000' +\n            '00101010111' + '11001100001' + '10011001101' + '00111011000'\n        )\n"
  },
  {
    "path": "test/test_bits.py",
    "content": "import unittest\n\nfrom pyqart.common.bits import Bits\n\n\nclass TestBits(unittest.TestCase):\n    def test_bits_as_string_when_no_data(self):\n        b = Bits()\n        self.assertEqual(b.as_string, '')\n\n    def test_bits_as_string_when_has_data(self):\n        b = Bits()\n        b.append(0b11010011, 8)\n        self.assertEqual(b.as_string, '11010011')\n        b.append(0b1000, 4)\n        self.assertEqual(b.as_string, '110100111000')\n\n    def test_bits_as_int_when_no_data(self):\n        b = Bits()\n        self.assertEqual(b.as_int, -1)\n\n    def test_bits_as_int_when_less_than_a_byte(self):\n        b = Bits()\n        b.append(1, 1)\n        self.assertEqual(b.as_int, 1)\n\n    def test_bits_as_int_when_between_one_and_two_byte(self):\n        b = Bits()\n        b.append(0b111100111, 9)\n        self.assertEqual(b.as_int, 0b111100111)\n\n    def test_bits_append_bit(self):\n        b = Bits()\n        b.append_bit(True)\n        b.append_bit(True)\n        b.append_bit(False)\n        b.append_bit(False)\n        self.assertEqual(b.as_int, 0b1100)\n        b.append_bit(True)\n        b.append_bit(False)\n        b.append_bit(False)\n        b.append_bit(False)\n        self.assertEqual(b.as_int, 0b11001000)\n\n    def test_bits_append(self):\n        b = Bits()\n        b.append(0xAC, 8)\n        self.assertEqual(b.as_int, 0xAC)\n        b.append(0xF, 4)\n        self.assertEqual(b.as_int, 0x0ACF)\n\n    def test_bits_extend_all(self):\n        b = Bits()\n        b.extend(b'\\xAC')\n        self.assertEqual(b.as_int, 0xAC)\n        b.extend(bytearray(b'\\x1F'))\n        self.assertEqual(b.as_int, 0x0AC1F)\n\n    def test_bits_extend_other_bits_all(self):\n        b = Bits()\n        b.extend(b'\\xAC')\n        other_bits = Bits.copy_from(b)\n        b.extend(other_bits)\n        self.assertEqual(b.as_int, 0xACAC)\n\n    def test_bits_extend_other_bytes_0_to_not_end(self):\n        b = Bits()\n        b.extend(b'\\x0F', count=6)\n        self.assertEqual(b.as_int, 3)\n\n    def test_bits_extend_other_bytes_not_start_to_end(self):\n        b = Bits()\n        b.extend(b'\\x0F', 4)\n        self.assertEqual(b.as_int, 15)\n\n    def test_bits_extend_other_bytes_not_start_to_not_end(self):\n        b = Bits()\n        b.extend(b'\\x0F', 4, 2)\n        self.assertEqual(b.as_int, 3)\n\n    def test_bits_xor(self):\n        b = Bits(0b001010001, 9)\n        o = Bits(0b0010110, 7)\n        b.xor(o, 2, 3, 3)\n        self.assertEqual(b.as_string, '001100001')\n\n        b = Bits(0b001010001, 9)\n        o = Bits(0b0010110, 7)\n        b.xor(o, 2, 3)\n        self.assertEqual(b.as_string, '001100001')\n"
  },
  {
    "path": "test/test_bits_utils.py",
    "content": "import unittest\n\nfrom pyqart.common.bit_funcs import one_at, zero_at, set_bit, bit_at\n\n\nclass TestBitUtils(unittest.TestCase):\n    def test_one_at_normal(self):\n        self.assertEqual(one_at(0), 0b10000000)\n        self.assertEqual(one_at(3), 0b00010000)\n        self.assertEqual(one_at(4), 0b00001000)\n        self.assertEqual(one_at(7), 0b00000001)\n        self.assertEqual(one_at(0, 12), 0b100000000000)\n        self.assertEqual(one_at(5, 10), 0b0000010000)\n        self.assertEqual(one_at(8, 10), 0b0000000010)\n\n    def test_one_at_fail(self):\n        with self.assertRaises(AssertionError):\n            one_at(-1)\n        with self.assertRaises(AssertionError):\n            one_at(-2)\n        with self.assertRaises(AssertionError):\n            one_at(8)\n        with self.assertRaises(AssertionError):\n            one_at(10, 8)\n        with self.assertRaises(AssertionError):\n            one_at(10, 9)\n\n    def test_zero_at_normal(self):\n        self.assertEqual(zero_at(0), 0b01111111)\n        self.assertEqual(zero_at(3), 0b11101111)\n        self.assertEqual(zero_at(4), 0b11110111)\n        self.assertEqual(zero_at(7), 0b11111110)\n        self.assertEqual(zero_at(10, 12), 0b111111111101)\n        self.assertEqual(zero_at(0, 12), 0b011111111111)\n        self.assertEqual(zero_at(7, 8), 0b11111110)\n\n    def test_zero_at_fail(self):\n        with self.assertRaises(AssertionError):\n            zero_at(-1)\n        with self.assertRaises(AssertionError):\n            zero_at(-2)\n        with self.assertRaises(AssertionError):\n            zero_at(8)\n        with self.assertRaises(AssertionError):\n            zero_at(10)\n        with self.assertRaises(AssertionError):\n            zero_at(10, 9)\n        with self.assertRaises(AssertionError):\n            zero_at(10, 10)\n\n    def test_set_bit_normal(self):\n        self.assertEqual(set_bit(0b01001010, 2, True), 0b01101010)\n        self.assertEqual(set_bit(0b01001010, 6, False), 0b01001000)\n\n    def test_set_bit_fail(self):\n        with self.assertRaises(AssertionError):\n            set_bit(0, -1, True)\n        with self.assertRaises(AssertionError):\n            set_bit(0, -2, True)\n        with self.assertRaises(AssertionError):\n            set_bit(0, 8, False)\n        with self.assertRaises(AssertionError):\n            set_bit(0, 9, False)\n\n    def test_bit_at_normal(self):\n        self.assertEqual(bit_at(0b01100, 5, 0), False)\n        self.assertEqual(bit_at(0b01100, 5, 1), True)\n        self.assertEqual(bit_at(0b101101, 6, 1), False)\n        self.assertEqual(bit_at(0b101101, 6, 5), True)\n        self.assertEqual(bit_at(0b11110111, 8, 4), False)\n        self.assertEqual(bit_at(0b11110111, 8, 7), True)\n\n    def test_bit_at_fail(self):\n        # value_upper = 0\n        with self.assertRaises(AssertionError):\n            self.assertEqual(bit_at(0, 0, 0), False)\n        # value_upper < 0\n        with self.assertRaises(AssertionError):\n            self.assertEqual(bit_at(0, -1, 0), False)\n        with self.assertRaises(AssertionError):\n            self.assertEqual(bit_at(0, -5, 0), False)\n"
  },
  {
    "path": "test/test_encode_numbers.py",
    "content": "import unittest\n\nfrom pyqart.qr.data.numbers import Numbers\n\n\nclass TestNumbers(unittest.TestCase):\n    def test_numbers_length_mod_3_is_0(self):\n        numbers = Numbers('923576', 10)\n        self.assertEqual(\n            numbers.output.as_string,\n            '0001' + '0000000110' +\n            '1110011011' + '1001000000',\n        )\n\n    def test_numbers_length_mod_3_is_1(self):\n        numbers = Numbers('0123456789012345', 10)\n        self.assertEqual(\n            numbers.output.as_string,\n            '0001' + '0000010000' +\n            '0000001100' + '0101011001' + '1010100110' +\n            '1110000101' + '0011101010' + '0101',\n        )\n\n    def test_numbers_length_mod_3_is_2(self):\n        numbers = Numbers('01234567', 10)\n        self.assertEqual(\n            numbers.output.as_string,\n            '0001' + '0000001000' + '000000110001010110011000011',\n        )"
  },
  {
    "path": "test/test_encode_raw.py",
    "content": "import unittest\n\nfrom pyqart.qr.data.raw import Raw\n\n\nclass TestRaw(unittest.TestCase):\n    def test_raw(self):\n        raw = Raw(b'Hello, world!', 8)\n        self.assertEqual(\n            raw.output.as_string,\n            '0100' + '00001101' +\n            '01001000' + '01100101' + '01101100' + '01101100' +\n            '01101111' + '00101100' + '00100000' + '01110111' +\n            '01101111' + '01110010' + '01101100' + '01100100' +\n            '00100001',\n        )\n"
  }
]