/* nma-bar-code.h - Renderer of a "QR" code
*
* Lubomir Rintel <lkundrak@v3.sk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the ree Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright 2018, 2019 Red Hat, Inc.
*/
#include "nm-default.h"
#include <stdint.h>
#include "nma-bar-code.h"
/*
* The aim of this class is to provide a GObject-y QR code generator based
* on qrcodegen library [1]. We purposefully include it directly instead
* of compiling it separately, while providing a much less flexible (and
* more straightforward) API. This way we the compiler does a good job at
* slimming things down (chopping off half of the library) while allowing
* us to leave the original source unmodified for easier maintenance.
*
* [1] https://github.com/nayuki/QR-Code-generator
*/
#pragma GCC visibility push(hidden)
NM_PRAGMA_WARNING_DISABLE("-Wdeclaration-after-statement")
#define NDEBUG
#include "qrcodegen.c"
NM_PRAGMA_WARNING_REENABLE
#pragma GCC visibility pop
struct _NMABarCode {
GObject parent;
};
struct _NMABarCodeClass {
GObjectClass parent_class;
};
typedef struct {
uint8_t qrcode[qrcodegen_BUFFER_LEN_FOR_VERSION (qrcodegen_VERSION_MAX)];
} NMABarCodePrivate;
/**
* SECTION:nma-bar-code
* @title: NMABarCode
*
* A Bar Code object provides the means of drawing a QR code onto a cairo
* context. Useful for rendering Wi-Fi network credential in a form that
* can be optically scanned with a phone camera.
*/
G_DEFINE_TYPE_WITH_CODE (NMABarCode, nma_bar_code, G_TYPE_OBJECT,
G_ADD_PRIVATE (NMABarCode))
enum {
PROP_0,
PROP_TEXT,
PROP_SIZE,
LAST_PROP
};
#define NMA_BAR_CODE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMA_TYPE_BAR_CODE, NMABarCodePrivate))
/**
* nma_bar_code_set_text:
* @self: bar code instance
* @text: new bar code text
*
* Regenerates the QR code for a different text.
*
* Since: 1.8.22
*/
void
nma_bar_code_set_text (NMABarCode *self, const char *text)
{
g_object_set (self, NMA_BAR_CODE_TEXT, text, NULL);
}
/**
* nma_bar_code_get_size:
* @self: bar code instance
*
* Returns: the side of a QR code square.
*
* Since: 1.8.22
*/
int
nma_bar_code_get_size (NMABarCode *self)
{
int size;
g_object_get (self, NMA_BAR_CODE_SIZE, &size, NULL);
return size;
}
/**
* nma_bar_code_draw:
* @self: bar code instance
* @cr: cairo context
*
* Draws the QR code onto the given context.
*
* Since: 1.8.22
*/
void
nma_bar_code_draw (NMABarCode *self, cairo_t *cr)
{
NMABarCodePrivate *priv = NMA_BAR_CODE_GET_PRIVATE (self);
int x, y, size;
size = qrcodegen_getSize (priv->qrcode);
cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
for (y = 0; y < size; y++) {
for (x = 0; x < size; x++) {
if (qrcodegen_getModule (priv->qrcode, x, y)) {
cairo_rectangle (cr, x, y, 1, 1);
cairo_fill (cr);
}
}
}
}
static void
get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
NMABarCodePrivate *priv = NMA_BAR_CODE_GET_PRIVATE (object);
switch (prop_id) {
case PROP_SIZE:
g_value_set_int (value, qrcodegen_getSize (priv->qrcode));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
NMABarCodePrivate *priv = NMA_BAR_CODE_GET_PRIVATE (object);
uint8_t tempBuffer[qrcodegen_BUFFER_LEN_FOR_VERSION (qrcodegen_VERSION_MAX)];
const char *text;
bool success = FALSE;
switch (prop_id) {
case PROP_TEXT:
text = g_value_get_string (value);
if (text) {
success = qrcodegen_encodeText(g_value_get_string (value),
tempBuffer,
priv->qrcode,
qrcodegen_Ecc_LOW,
qrcodegen_VERSION_MIN,
qrcodegen_VERSION_MAX,
qrcodegen_Mask_AUTO,
FALSE);
}
if (!success)
bzero (priv->qrcode, sizeof (priv->qrcode));
g_object_notify (object, NMA_BAR_CODE_SIZE);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
nma_bar_code_init (NMABarCode *self)
{
}
/**
* nma_bar_code_new:
* @text: set the bar code text
*
* Returns: (transfer full): the bar code instance
*
* Since: 1.8.22
*/
NMABarCode *
nma_bar_code_new (const char *text)
{
return g_object_new (NMA_TYPE_BAR_CODE, NMA_BAR_CODE_TEXT, text, NULL);
}
static void
nma_bar_code_class_init (NMABarCodeClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = get_property;
object_class->set_property = set_property;
g_object_class_install_property
(object_class, PROP_TEXT,
g_param_spec_string (NMA_BAR_CODE_TEXT, "", "",
"", G_PARAM_WRITABLE));
g_object_class_install_property
(object_class, PROP_SIZE,
g_param_spec_int (NMA_BAR_CODE_SIZE, "", "",
G_MININT, G_MAXINT, 0, G_PARAM_READABLE));
}