1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
### BEGIN LICENSE
# Copyright (C) 2012 David Planella <david.planella@ubuntu.com>
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
### END LICENSE
import sys
try:
import qrencode
except ImportError:
print "You need to install the python-qrencode package"
sys.exit(1)
import Image
import ImageOps
import cairo
import array
class QRCodeSize(object):
SMALL = 120
MEDIUM = 230
LARGE = 350
class QRCodeOutput(object):
PIL_IMAGE = 0
CAIRO_SURFACE = 1
class QRCode(object):
def __init__(self, output_size=QRCodeSize.MEDIUM):
self.output_size = output_size
self.qrcode_size = 0
self.pixel_size = 1
self.image = None
self.cr_surface = None
def encode(self, text, current_color_fg=None, previous_color_fg=None,
current_color_bg=None, previous_color_bg=None, border=True,
output_type=QRCodeOutput.PIL_IMAGE):
'''Encodes the given text and returns a QR code in the given format'''
if output_type == QRCodeOutput.PIL_IMAGE:
qr_code = self._encode_to_pil(text, current_color_fg,
previous_color_fg, current_color_bg, previous_color_bg,
border)
elif output_type == QRCodeOutput.CAIRO_SURFACE:
qr_code = self._encode_to_cairo(text, current_color_fg,
previous_color_fg, current_color_bg, previous_color_bg,
border)
return qr_code
def _encode_to_cairo(self, text, current_color_fg=None,
previous_color_fg=None, current_color_bg=None,
previous_color_bg=None, border=True):
'''Encodes the given text and returns a Cairo surface'''
self._encode_to_pil(text, current_color_fg, previous_color_fg,
current_color_bg, previous_color_bg, border)
# Convert the Python PIL image to a Cairo surface.
# Notice the conversion to BGRA that Cairo expects
# See http://cairographics.org/pythoncairopil/ and
# http://mail.gnome.org/archives/gtkmm-list/2007-May/msg00111.html
bytearr = array.array('B', self.image.tobytes("raw", "BGRA", 0, 1))
height, width = self.image.size
#print self.image.size
self.cr_surface = cairo.ImageSurface.create_for_data(bytearr,
cairo.FORMAT_ARGB32,
width, height,
width * 4)
return self.cr_surface
def _encode_to_pil(self, text, current_color_fg=None,
previous_color_fg=None, current_color_bg=None,
previous_color_bg=None, border=True):
'''Encodes the given text and returns a Python PIL Image,
changing colors if necessary'''
# Returns an 8-bit pixel, black and white image (mode L)
version, self.qrcode_size, self.image = qrencode.encode(text)
if (current_color_fg != previous_color_fg) or \
(current_color_bg != previous_color_bg):
self.image = ImageOps.colorize(self.image,
(current_color_fg[0],
current_color_fg[1],
current_color_fg[2]),
(current_color_bg[0],
current_color_bg[1],
current_color_bg[2]))
# Returns a 4x8-bit pixel, true colour with transparency mask image
# (mode RGBA)
self.image = self.image.convert('RGBA')
self._resize()
if border:
self._add_border(current_color_bg)
return self.image
def _resize(self):
self._set_pixel_size(self.qrcode_size)
#print self.pixel_size, self.qrcode_size
self.image = self.image.resize((self.pixel_size * self.qrcode_size,
self.pixel_size * self.qrcode_size),
Image.NEAREST)
def _set_pixel_size(self, barcode_orig_size):
pixel_size = 1
#print pixel_size * barcode_orig_size, self.output_size
while (pixel_size * barcode_orig_size < self.output_size):
pixel_size += 1
while ((pixel_size * barcode_orig_size > self.output_size) or \
((pixel_size * barcode_orig_size) % 2 != 0) or \
(pixel_size * barcode_orig_size + 2 * pixel_size > \
self.output_size)):
pixel_size -= 1
# FIXME: it needs to automatically go up to the next size,
# rather than hardcoding it as now
if pixel_size == 0:
self.output_size = QRCodeSize.MEDIUM
self.pixel_size = pixel_size
def _add_border(self, current_color_bg=None):
'''Adds a border to the QR code'''
if current_color_bg:
fill = (current_color_bg[0], current_color_bg[1],
current_color_bg[2], 255)
else:
fill = 'white'
# Add a border
border_size = (self.output_size - self.image.size[0]) / 2
self.image = ImageOps.expand(self.image, border=border_size,
fill=fill)
|