Skip to content

Commit

Permalink
Added option to add label AND image
Browse files Browse the repository at this point in the history
  • Loading branch information
werthdavid committed Oct 4, 2020
1 parent 029046a commit 10aec52
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 56 deletions.
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ This can reduce the readability of the code!
### Image as Code
<img src="https://raw.githubusercontent.com/werthdavid/kjua/master/docs/image-as-code.png"/>

### Labelimage and Imagelabel
Use this, if you want a label AND an image. In these modes `mSize`, `mPosX` and `mPosY` can be provided as an array.
In mode `labelimage`, the first value (index 0) of the `mSize`, `mPosX` and `mPosY` arrays is used for the label,
the second value (index 1) is used for image and vice versa. Also in `labelimage` mode, the label is drawn before the
image is drawn and therefore kinda "in the background" if the two overlap.

### All options

Expand All @@ -66,10 +71,10 @@ This can reduce the readability of the code!
* `back` background color (defaults to `#fff`, for transparent use `''` or `null`)
* `rounded` roundend corners in pc: 0..100 (defaults to `0`, not working if `render`is set to `svg`)
* `quiet` quiet zone in modules (defaults to `0`)
* `mode` modes: 'plain', 'label' or 'image' (defaults to `plain`, set `label` or `image` property if you change this)
* `mSize` label/image size in pc: 0..100 (defaults to `30`)
* `mPosX` label/image pos x in pc: 0..100 (defaults to `50`)
* `mPosY` label/image pos y in pc: 0..100 (defaults to `50`)
* `mode` modes: 'plain', 'label', 'image', 'imagelabel' or 'labelimage' (defaults to `plain`, set `label` or `image` property if you change this)
* `mSize` label/image size in pc: 0..100 (defaults to `30`) or a number-array if `mode` is 'imagelabel' or 'labelimage'
* `mPosX` label/image pos x in pc: 0..100 (defaults to `50`) or a number-array if `mode` is 'imagelabel' or 'labelimage'
* `mPosY` label/image pos y in pc: 0..100 (defaults to `50`) or a number-array if `mode` is 'imagelabel' or 'labelimage'
* `label` additional label text (defaults to ``)
* `fontname` font for additional label text (defaults to `sans-serif`)
* `fontcolor` font-color for additional label text (defaults to `#333`)
Expand All @@ -83,5 +88,6 @@ More details can be found on [larsjung.de/kjua](https://larsjung.de/kjua/)

* ~~possibility to render QR-codes as SVG~~
* image can be provided as base64-string
* Typescript-types
* draw the image as part of the code --> `imageAsCode`
* new `modes`: `labelimage` and `imagelabel`
* Typescript-types
14 changes: 11 additions & 3 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,20 @@
<option value="plain">plain</option>
<option selected="selected" value="label">label</option>
<option value="image">image</option>
<option value="imagelabel">imagelabel</option>
<option value="labelimage">labelimage</option>
</select>
<hr>
<label for="msize">SIZE:</label><input id="msize" max="100" min="0" step="1" type="range" value="30"><label for="mposx">POS X:</label><input id="mposx" max="100" min="0" step="1" type="range"
value="50"><label for="mposy">POS Y:</label><input
id="mposy" max="100" min="0" step="1" type="range" value="50">
<label for="msize">SIZE:</label><input id="msize" max="100" min="0" step="1" type="range" value="30">
<label for="mposx">POS X:</label><input id="mposx" max="100" min="0" step="1" type="range" value="50">
<label for="mposy">POS Y:</label><input id="mposy" max="100" min="0" step="1" type="range" value="50">
<hr>
<div id="pos2" style="display: none">
<label for="msize2">SIZE 2:</label><input id="msize2" max="100" min="0" step="1" type="range" value="30">
<label for="mposx2">POS X 2:</label><input id="mposx2" max="100" min="0" step="1" type="range" value="50">
<label for="mposy2">POS Y 2:</label><input id="mposy2" max="100" min="0" step="1" type="range" value="50">
<hr>
</div>
<label for="label">LABEL</label><input id="label" type="text" value="kjua">
<label for="font">FONT NAME</label><input id="font" type="text" value="Ubuntu">
<label for="fontcolor">FONT COLOR</label><input id="fontcolor" type="color" value="#ff9818">
Expand Down
4 changes: 2 additions & 2 deletions docs/kjua.min.js

Large diffs are not rendered by default.

25 changes: 20 additions & 5 deletions docs/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
['rounded', '%'],
['msize', '%'],
['mposx', '%'],
['mposy', '%']
['mposy', '%'],
['msize2', '%'],
['mposx2', '%'],
['mposy2', '%']
];

function el_by_id(id) {
Expand Down Expand Up @@ -66,6 +69,18 @@
if (image === "") {
image = el_by_id('img-buffer');
}
let mode = val_by_id('mode');
let mSize = int_by_id('msize');
let mPosX = int_by_id('mposx');
let mPosY = int_by_id('mposy');
if (mode === "labelimage" || mode === "imagelabel") {
mSize = [int_by_id('msize'), int_by_id('msize2')];
mPosX = [int_by_id('mposx'), int_by_id('mposx2')];
mPosY = [int_by_id('mposy'), int_by_id('mposy2')];
document.getElementById("pos2").style.display = "";
} else {
document.getElementById("pos2").style.display = "none";
}

var options = {
render: val_by_id('render'),
Expand All @@ -81,11 +96,11 @@
rounded: int_by_id('rounded'),
quiet: int_by_id('quiet'),

mode: val_by_id('mode'),
mode,

mSize: int_by_id('msize'),
mPosX: int_by_id('mposx'),
mPosY: int_by_id('mposy'),
mSize,
mPosX,
mPosY,

label: val_by_id('label'),
fontname: val_by_id('font'),
Expand Down
10 changes: 5 additions & 5 deletions kjua.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,16 @@ declare namespace kjua {
quiet?: number;

/**
* modes: 'plain', 'label' or 'image'
* modes: 'plain', 'label', 'image', 'imagelabel' or 'labelimage'
*/
mode?: 'plain' | 'label' | 'image';
mode?: 'plain' | 'label' | 'image' | 'imagelabel' | 'labelimage';

/**
* label/image size and pos in pc: 0..100
*/
mSize?: number;
mPosX?: number;
mPosY?: number;
mSize?: number | [number];
mPosX?: number | [number];
mPosY?: number | [number];

/**
* label
Expand Down
6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "kjua-svg",
"version": "1.11.0",
"version": "1.12.0",
"description": "Dynamically generated QR codes for modern browsers.",
"homepage": "https://github.com/werthdavid/kjua",
"bugs": "https://github.com/werthdavid/kjua/issues",
Expand Down Expand Up @@ -30,15 +30,13 @@
"devDependencies": {
"@babel/core": "^7.11.6",
"@babel/preset-env": "^7.11.5",
"copyfiles": "^2.4.0",
"eslint": "^7.10.0",
"ghu": "^0.26.0",
"qrcode-generator": "^1.4.4",
"scar": "^2.3.0"
},
"engines": {
"node": ">=10.0.0"
},
"dependencies": {
"copyfiles": "^2.4.0"
}
}
75 changes: 53 additions & 22 deletions src/lib/create_svg_qrcode.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,18 @@ const create_draw_ctx = ctx => {
const rnd = x => Math.round(x * 10) / 10;
const rndo = x => Math.round(x * 10) / 10 + ctx.o;
return {
m(x, y) {ctx.p += `M ${rndo(x)} ${rndo(y)} `; return this;},
l(x, y) {ctx.p += `L ${rndo(x)} ${rndo(y)} `; return this;},
a(x, y, rad) {ctx.p += `A ${rnd(rad)} ${rnd(rad)} 0 0 1 ${rndo(x)} ${rndo(y)} `; return this;}
m(x, y) {
ctx.p += `M ${rndo(x)} ${rndo(y)} `;
return this;
},
l(x, y) {
ctx.p += `L ${rndo(x)} ${rndo(y)} `;
return this;
},
a(x, y, rad) {
ctx.p += `A ${rnd(rad)} ${rnd(rad)} 0 0 1 ${rndo(x)} ${rndo(y)} `;
return this;
}
};
};

Expand Down Expand Up @@ -114,24 +123,41 @@ const create_path = (qr, settings) => {
};

const add_label = (el, settings) => {
let mSize = settings.mSize;
let mPosX = settings.mPosX;
let mPosY = settings.mPosY;
let arrayPos = 0;
if (settings.mode === "imagelabel") {
arrayPos = 1;
}
if (Array.isArray(settings.mSize)) {
mSize = settings.mSize[arrayPos];
}
if (Array.isArray(settings.mPosX)) {
mPosX = settings.mPosX[arrayPos];
}
if (Array.isArray(settings.mPosY)) {
mPosY = settings.mPosY[arrayPos];
}

const size = settings.size;
const font = 'bold ' + settings.mSize * 0.01 * size + 'px ' + settings.fontname;
const font = 'bold ' + mSize * 0.01 * size + 'px ' + settings.fontname;

const dom = require('./dom');
const ratio = settings.ratio || dom.dpr;
const ctx = dom.create_canvas(size, ratio).getContext('2d');
ctx.strokeStyle = settings.back;
ctx.lineWidth = settings.mSize * 0.01 * size * 0.1;
ctx.lineWidth = mSize * 0.01 * size * 0.1;
ctx.fillStyle = settings.fontcolor;
ctx.font = font;
const w = ctx.measureText(settings.label).width;

const sh = settings.mSize * 0.01;
const sh = mSize * 0.01;
const sw = w / size;
const sl = (1 - sw) * settings.mPosX * 0.01;
const st = (1 - sh) * settings.mPosY * 0.01;
const sl = (1 - sw) * mPosX * 0.01;
const st = (1 - sh) * mPosY * 0.01;
const x = sl * size;
const y = st * size + 0.75 * settings.mSize * 0.01 * size;
const y = st * size + 0.75 * mSize * 0.01 * size;

const text_el = create_svg_el('text', {
x,
Expand Down Expand Up @@ -200,23 +226,28 @@ const create_svg_qrcode = (qr, settings) => {
fill: settings.fill
}));


if (settings.imageAsCode) {
const ratio = settings.ratio || dpr;
const canvas = create_canvas(settings.size, ratio);
const ctx2 = canvas.getContext('2d');
draw_modules(qr, ctx2, settings);
const imagePos = calc_image_pos(settings);
ctx2.globalCompositeOperation = "source-in";
ctx2.drawImage(settings.image, imagePos.x, imagePos.y, imagePos.iw, imagePos.ih);
settings = Object.assign({}, settings, {image: ctx2.canvas.toDataURL()});
} else {
settings = Object.assign({}, settings, {image: get_attr(settings.image, 'src')});
}
if (mode === 'label') {
add_label(svg_el, settings);
} else if (mode === 'image') {
if (settings.imageAsCode) {
const ratio = settings.ratio || dpr;
const canvas = create_canvas(settings.size, ratio);
const ctx2 = canvas.getContext('2d');
draw_modules(qr, ctx2, settings);
const imagePos = calc_image_pos(settings);
ctx2.globalCompositeOperation = "source-in";
ctx2.drawImage(settings.image, imagePos.x, imagePos.y, imagePos.iw, imagePos.ih);
settings = Object.assign({}, settings, {image: ctx2.canvas.toDataURL()});
} else {
settings = Object.assign({}, settings, {image: get_attr(settings.image, 'src')});
}
add_image(svg_el, settings);
} else if (mode === 'labelimage') {
add_label(svg_el, settings);
add_image(svg_el, settings);
} else if (mode === 'imagelabel') {
add_image(svg_el, settings);
add_label(svg_el, settings);
}

return svg_el;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ module.exports = {
// quiet zone in modules
quiet: 0,

// modes: 'plain', 'label' or 'image'
// modes: 'plain', 'label', 'image', 'imagelabel' or 'labelimage'
mode: 'plain',

// label/image size and pos in pc: 0..100
Expand Down
22 changes: 19 additions & 3 deletions src/lib/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,29 @@ const canvas_to_img = canvas => {
};

const calc_image_pos = (settings) => {
let mSize = settings.mSize;
let mPosX = settings.mPosX;
let mPosY = settings.mPosY;
let arrayPos = 0;
if (settings.mode === "labelimage") {
arrayPos = 1;
}
if (Array.isArray(settings.mSize)) {
mSize = settings.mSize[arrayPos];
}
if (Array.isArray(settings.mPosX)) {
mPosX = settings.mPosX[arrayPos];
}
if (Array.isArray(settings.mPosY)) {
mPosY = settings.mPosY[arrayPos];
}
const size = settings.size;
const w = settings.image.naturalWidth || 1;
const h = settings.image.naturalHeight || 1;
const sh = settings.mSize * 0.01;
const sh = mSize * 0.01;
const sw = sh * w / h;
const sl = (1 - sw) * settings.mPosX * 0.01;
const st = (1 - sh) * settings.mPosY * 0.01;
const sl = (1 - sw) * mPosX * 0.01;
const st = (1 - sh) * mPosY * 0.01;
const x = sl * size;
const y = st * size;
const iw = sw * size;
Expand Down
35 changes: 29 additions & 6 deletions src/lib/draw_mode.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
const {calc_image_pos} = require('./dom');

const draw_label = (ctx, settings) => {
let mSize = settings.mSize;
let mPosX = settings.mPosX;
let mPosY = settings.mPosY;
let arrayPos = 0;
if (settings.mode === "imagelabel") {
arrayPos = 1;
}
if (Array.isArray(settings.mSize)) {
mSize = settings.mSize[arrayPos];
}
if (Array.isArray(settings.mPosX)) {
mPosX = settings.mPosX[arrayPos];
}
if (Array.isArray(settings.mPosY)) {
mPosY = settings.mPosY[arrayPos];
}

const size = settings.size;
const font = 'bold ' + settings.mSize * 0.01 * size + 'px ' + settings.fontname;
const font = 'bold ' + mSize * 0.01 * size + 'px ' + settings.fontname;

ctx.strokeStyle = settings.back;
ctx.lineWidth = settings.mSize * 0.01 * size * 0.1;
ctx.lineWidth = mSize * 0.01 * size * 0.1;
ctx.fillStyle = settings.fontcolor;
ctx.font = font;

const w = ctx.measureText(settings.label).width;
const sh = settings.mSize * 0.01;
const sh = mSize * 0.01;
const sw = w / size;
const sl = (1 - sw) * settings.mPosX * 0.01;
const st = (1 - sh) * settings.mPosY * 0.01;
const sl = (1 - sw) * mPosX * 0.01;
const st = (1 - sh) * mPosY * 0.01;
const x = sl * size;
const y = st * size + 0.75 * settings.mSize * 0.01 * size;
const y = st * size + 0.75 * mSize * 0.01 * size;

ctx.strokeText(settings.label, x, y);
ctx.fillText(settings.label, x, y);
Expand All @@ -36,6 +53,12 @@ const draw_mode = (ctx, settings) => {
draw_label(ctx, settings);
} else if (mode === 'image') {
draw_image(ctx, settings);
} else if (mode === 'labelimage') {
draw_label(ctx, settings);
draw_image(ctx, settings);
} else if (mode === 'imagelabel') {
draw_image(ctx, settings);
draw_label(ctx, settings);
}
};

Expand Down

0 comments on commit 10aec52

Please sign in to comment.