You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

246 lines
7.6 KiB

#include <Rcpp.h>
#include "qrencode.h"
#include <stdio.h>
#include <unistd.h>
#include <string>
#include <fstream>
#include <streambuf>
using namespace Rcpp;
#define INCHES_PER_METER (100.0/2.54)
static int rle = 1;
static unsigned int fg_color[4] = {0, 0, 0, 255};
static unsigned int bg_color[4] = {255, 255, 255, 255};
//' @md
//' @title Encodes a string as a QR code
//' @description Encodes a string as a QR coder
//' @param to_encode character string to encode
//' @param version version of the symbol. If `0`, the library chooses the
//' minimum version for the given input data.
//' @param level error correction level (`0` - `3`, lowest to highest)
//' @param hint tell the library how Japanese Kanji characters should be
//' encoded:
//' - If "`3`", the library assumes that the given string contains Shift-JIS characters
//' and encodes them in Kanji-mode.
//' - If "`2`" is given, all of non-alphanumerical characters will be encoded as is.
//' If you want to embed UTF-8 string, choose this. Trying to encode UTF-8 with modes will cause an error.
//' - "`0`" is "numeric mode",
//' - "`1`" is "alphanumeric mode"
//' - "`5`" is "ECI mode".
//' @param caseinsensitive case-sensitive(\code{1}) or not(\code{0}).
//' @seealso \url{http://www.qrcode.com/en/about/version.html}
//' @export
// [[Rcpp::export]]
NumericMatrix qrencode_raw(std::string to_encode,
int version=0,
int level=0,
int hint=2,
int caseinsensitive=1) {
QRcode *qrcode ;
unsigned char *row;
int x, y;
qrcode = QRcode_encodeString(to_encode.c_str(),
version,
(QRecLevel)level,
(QRencodeMode)hint, caseinsensitive);
NumericMatrix qr(qrcode->width, qrcode->width);
for(y=0; y <qrcode->width; y++) {
row = qrcode->data+(y*qrcode->width);
for(x = 0; x < qrcode->width; x++) {
qr(x, y) = row[x]&0x1;
}
}
return(qr);
}
static FILE *openFile(const char *outfile) {
FILE *fp;
if(outfile == NULL || (outfile[0] == '-' && outfile[1] == '\0')) {
fp = stdout;
} else {
fp = fopen(outfile, "wb");
if (fp == NULL) return(NULL);
}
return fp;
}
static void writeSVG_writeRect(FILE *fp, int x, int y, int width, char* col, float opacity) {
if(fg_color[3] != 255) {
fprintf(fp, "\t\t\t<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"1\" "\
"fill=\"#%s\" fill-opacity=\"%f\" />\n",
x, y, width, col, opacity );
} else {
fprintf(fp, "\t\t\t<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"1\" "\
"fill=\"#%s\" />\n",
x, y, width, col );
}
}
CharacterVector writeSVG(QRcode *qrcode, int margin, int size, int dpi) {
FILE *fp;
unsigned char *row, *p;
int x, y, x0, pen;
int symwidth, realwidth;
float scale;
char fg[7], bg[7];
float fg_opacity;
float bg_opacity;
char fname[L_tmpnam];
memset(fname, 0, L_tmpnam);
strncpy(fname,"qrencoder-XXXXXX", 16);
fp = openFile(mktemp(fname));
if (fp == NULL) return(R_NilValue);
scale = dpi * INCHES_PER_METER / 100.0;
symwidth = qrcode->width + margin * 2;
realwidth = symwidth * size;
snprintf(fg, 7, "%02x%02x%02x", fg_color[0], fg_color[1], fg_color[2]);
snprintf(bg, 7, "%02x%02x%02x", bg_color[0], bg_color[1], bg_color[2]);
fg_opacity = (float)fg_color[3] / 255;
bg_opacity = (float)bg_color[3] / 255;
/* XML declaration */
fputs( "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n", fp );
/* DTD
No document type specified because "while a DTD is provided in [the SVG]
specification, the use of DTDs for validating XML documents is known to be
problematic. In particular, DTDs do not handle namespaces gracefully. It
is *not* recommended that a DOCTYPE declaration be included in SVG
documents."
http://www.w3.org/TR/2003/REC-SVG11-20030114/intro.html#Namespace
*/
/* Vanity remark */
fprintf( fp, "<!-- Created with qrencode %s (http://fukuchi.org/works/qrencode/index.html.en) -->\n",
QRcode_APIVersionString() );
/* SVG code start */
fprintf( fp, "<svg width=\"%0.2fcm\" height=\"%0.2fcm\" viewBox=\"0 0 %d %d\""\
" preserveAspectRatio=\"none\" version=\"1.1\"" \
" xmlns=\"http://www.w3.org/2000/svg\">\n",
realwidth / scale, realwidth / scale, symwidth, symwidth
);
/* Make named group */
fputs( "\t<g id=\"QRcode\">\n", fp );
/* Make solid background */
if(bg_color[3] != 255) {
fprintf(fp, "\t\t<rect x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" fill=\"#%s\" fill-opacity=\"%f\" />\n", symwidth, symwidth, bg, bg_opacity);
} else {
fprintf(fp, "\t\t<rect x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" fill=\"#%s\" />\n", symwidth, symwidth, bg);
}
/* Create new viewbox for QR data */
fputs( "\t\t<g id=\"Pattern\">\n", fp);
/* Write data */
p = qrcode->data;
for(y=0; y<qrcode->width; y++) {
row = (p+(y*qrcode->width));
if( !rle ) {
/* no RLE */
for(x=0; x<qrcode->width; x++) {
if(*(row+x)&0x1) {
writeSVG_writeRect(fp, margin + x,
margin + y, 1,
fg, fg_opacity);
}
}
} else {
/* simple RLE */
pen = 0;
x0 = 0;
for(x=0; x<qrcode->width; x++) {
if( !pen ) {
pen = *(row+x)&0x1;
x0 = x;
} else {
if(!(*(row+x)&0x1)) {
writeSVG_writeRect(fp, x0 + margin, y + margin, x-x0, fg, fg_opacity);
pen = 0;
}
}
}
if( pen ) {
writeSVG_writeRect(fp, x0 + margin, y + margin, qrcode->width - x0, fg, fg_opacity);
}
}
}
/* Close QR data viewbox */
fputs( "\t\t</g>\n", fp );
/* Close group */
fputs( "\t</g>\n", fp );
/* Close SVG code */
fputs( "</svg>\n", fp );
fclose( fp );
std::ifstream t(fname);
std::string str((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
t.close();
unlink(fname);
return(Rcpp::wrap(str));
}
//' @md
//' @title Return a QR encoded string as an svg string
//' @description Encodes a string as a QR coder
//' @param to_encode character string to encode
//' @param version version of the symbol. If `0`, the library chooses the
//' minimum version for the given input data.
//' @param level error correction level (`0` - `3`, lowest to highest)
//' @param hint tell the library how Japanese Kanji characters should be
//' encoded:
//' - If "`3`", the library assumes that the given string contains Shift-JIS characters
//' and encodes them in Kanji-mode.
//' - If "`2`" is given, all of non-alphanumerical characters will be encoded as is.
//' If you want to embed UTF-8 string, choose this. Trying to encode UTF-8 with modes will cause an error.
//' - "`0`" is "numeric mode",
//' - "`1" is "alphanumeric mode"
//' - "`5`" is "ECI mode".
//' @param caseinsensitive case-sensitive(`1`) or not(`0`).
//' @param margin width of the marginsl default is 4
//' @param size module size in dots (pixels); default is 3
//' @param dpi resolution; default = 72
//' @seealso \url{http://www.qrcode.com/en/about/version.html}
//' @export
// [[Rcpp::export]]
CharacterVector qrencode_svg(
std::string to_encode,
int version=0, int level=0, int hint=2,
int caseinsensitive=1, int margin = 0, int size = 3, int dpi = 72) {
QRcode *qrcode ;
qrcode = QRcode_encodeString(to_encode.c_str(),
version,
(QRecLevel)level,
(QRencodeMode)hint, caseinsensitive);
return(writeSVG(qrcode, margin, size, dpi));
}