~thomas-deruyter-3/qreator/qreator

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
# -*- 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 qrencode
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, 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)
        elif output_type == QRCodeOutput.CAIRO_SURFACE:
            qr_code = self._encode_to_cairo(text)

        return qr_code

    def _encode_to_cairo(self, text):
        '''Encodes the given text and returns a Cairo surface'''
        self._encode_to_pil(text)

        # 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.tostring("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):
        '''Encodes the given text and returns a Python PIL Image'''
        version, self.qrcode_size, self.image = qrencode.encode(text)
        self.image = self.image.convert('RGBA')
        self._resize()
        self._add_border()

        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):
        '''Adds a border to the QR code'''
        # Add a border
        border_size = (self.output_size - self.image.size[0]) / 2
        self.image = ImageOps.expand(self.image, border=border_size,
                                     fill='white')