This topic is locked

Guide 32 - 2D Barcode Reader

1/14/2022 5:40:56 PM
PHPRunner Tips and Tricks
fhumanes author

img alt
As when I provided an example to generate QR codes, when providing an example to read QR codes , requests have arisen from readers to do something similar for 2D barcodes.

I have followed the same method that I used for QRs. I've been looking at examples and almost everything I've seen was sending me to this Quagga page .

The possibilities are almost endless. It has not been easy for me to tell myself which example to use, nor to adjust it to the functionality that I wanted the example to have.
Target

To be able to read 2D barcodes in the applications made in PHPRunner. Mainly, so that using a mobile we can produce information input from a barcode.

DEMO : https://fhumanes.com/scannerBarcode/

If you like this information, keep reading the article at this link

fhumanes author 1/14/2022

The aspect of the application is:

img alt
You see that the solution uses a rectangle to set the barcode detection and also uses a red stripe to identify when it has detected the code information.

The options presented by the example can be hidden by means of an HTML attribute, and thus not be customizable. Also, you can indicate which are the default options that you want to leave.

The snippet code is:

$html =<<<EOT
<link rel="stylesheet" href="MyCode/quagga.css">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">

<div class="wrapper">
<div id="container" class="container">
<div class="controls">
<fieldset class="reader-config-group">
<button class="stop">Stop</button>

<label>
<span>Barcode-Type</span>
<select name="decoder_readers">
<option value="code_128" selected="selected">Code 128</option>
<option value="code_39">Code 39</option>
<option value="code_39_vin">Code 39 VIN</option>
<option value="ean">EAN</option>
<option value="ean_extended">EAN-extended</option>
<option value="ean_8">EAN-8</option>
<option value="upc">UPC</option>
<option value="upc_e">UPC-E</option>
<option value="codabar">Codabar</option>
<option value="i2of5">I2of5</option>
<option value="2of5">Standard 2 of 5</option>
<option value="code_93">Code 93</option>
</select>
</label>
<label>
<span>Resolution (long side)</span>
<select name="input-stream_constraints">
<option value="320x240">320px</option>
<option selected="selected" value="640x480">640px</option>
<option value="800x600">800px</option>
<option value="1280x720">1280px</option>
<option value="1600x960">1600px</option>
<option value="1920x1080">1920px</option>
</select>
</label>
<label>
<span>Patch-Size</span>
<select name="locator_patch-size">
<option value="x-small">x-small</option>
<option value="small">small</option>
<option selected="selected" value="medium">medium</option>
<option value="large">large</option>
<option value="x-large">x-large</option>
</select>
</label>
<label>
<span>Half-Sample</span>
<input type="checkbox" checked="checked" name="locator_half-sample" />
</label>
<label>
<span>Workers</span>
<select name="numOfWorkers">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option selected="selected" value="4">4</option>
<option value="8">8</option>
</select>
</label>
<label>
<span>Camera</span>
<select name="input-stream_constraints" id="deviceSelection">
</select>
</label>
<label style="display: none">
<span>Zoom</span>
<select name="settings_zoom"></select>
</label>
<label style="display: none">
<span>Torch</span>
<input type="checkbox" name="settings_torch" />
</label>
</fieldset>
</div>
<div id="interactive" class="viewport"></div>
</div>
</div>
<script src="MyCode/adapter.js" type="text/javascript"></script>
<script src="MyCode/quagga.min.js" type="text/javascript"></script>
<script src="MyCode/scale.fix.js"></script>

EOT;
echo $html;

The JavaScript code is:

var ctrlBarcode = Runner.getControl(pageid, 'text1');
ctrlBarcode.makeReadonly();

$(function () {
var App = {
init: function () {
Quagga.init(this.state, function (err) {
if (err) {
console.log(err);
return;
}
App.attachListeners();
App.checkCapabilities();
Quagga.start();
});
},
checkCapabilities: function () {
var track = Quagga.CameraAccess.getActiveTrack();
var capabilities = {};
if (typeof track.getCapabilities === 'function') {
capabilities = track.getCapabilities();
}
this.applySettingsVisibility('zoom', capabilities.zoom);
this.applySettingsVisibility('torch', capabilities.torch);
},
updateOptionsForMediaRange: function (node, range) {
console.log('updateOptionsForMediaRange', node, range);
var NUM_STEPS = 6;
var stepSize = (range.max - range.min) / NUM_STEPS;
var option;
var value;
while (node.firstChild) {
node.removeChild(node.firstChild);
}
for (var i = 0; i <= NUM_STEPS; i++) {
value = range.min + (stepSize * i);
option = document.createElement('option');
option.value = value;
option.innerHTML = value;
node.appendChild(option);
}
},
applySettingsVisibility: function (setting, capability) {
// depending on type of capability
if (typeof capability === 'boolean') {
var node = document.querySelector('input[name="settings_' + setting + '"]');
if (node) {
node.parentNode.style.display = capability ? 'block' : 'none';
}
return;
}
if (window.MediaSettingsRange && capability instanceof window.MediaSettingsRange) {
var node = document.querySelector('select[name="settings_' + setting + '"]');
if (node) {
this.updateOptionsForMediaRange(node, capability);
node.parentNode.style.display = 'block';
}
return;
}
},
initCameraSelection: function () {
var streamLabel = Quagga.CameraAccess.getActiveStreamLabel();

return Quagga.CameraAccess.enumerateVideoDevices()
.then(function (devices) {
function pruneText(text) {
return text.length > 30 ? text.substr(0, 30) : text;
}
var $deviceSelection = document.getElementById("deviceSelection");
while ($deviceSelection.firstChild) {
$deviceSelection.removeChild($deviceSelection.firstChild);
}
devices.forEach(function (device) {
var $option = document.createElement("option");
$option.value = device.deviceId || device.id;
$option.appendChild(document.createTextNode(pruneText(device
.label || device.deviceId || device.id)));
$option.selected = streamLabel === device.label;
$deviceSelection.appendChild($option);
});
});
},
attachListeners: function () {
var self = this;

self.initCameraSelection();
$(".controls").on("click", "button.stop", function (e) {
e.preventDefault();
Quagga.stop();
});

$(".controls .reader-config-group").on("change", "input, select", function (e) {
e.preventDefault();
var $target = $(e.target),
value = $target.attr("type") === "checkbox" ? $target.prop(
"checked") : $target.val(),
name = $target.attr("name"),
state = self._convertNameToState(name);

console.log("Value of " + state + " changed to " + value);
self.setState(state, value);
});
},
_accessByPath: function (obj, path, val) {
var parts = path.split('.'),
depth = parts.length,
setter = (typeof val !== "undefined") ? true : false;

return parts.reduce(function (o, key, i) {
if (setter && (i + 1) === depth) {
if (typeof o[key] === "object" && typeof val === "object") {
Object.assign(o[key], val);
} else {
o[key] = val;
}
}
return key in o ? o[key] : {};
}, obj);
},
_convertNameToState: function (name) {
return name.replace("_", ".").split("-").reduce(function (result, value) {
return result + value.charAt(0).toUpperCase() + value.substring(1);
});
},
detachListeners: function () {
$(".controls").off("click", "button.stop");
$(".controls .reader-config-group").off("change", "input, select");
},
applySetting: function (setting, value) {
var track = Quagga.CameraAccess.getActiveTrack();
if (track && typeof track.getCapabilities === 'function') {
switch (setting) {
case 'zoom':
return track.applyConstraints({
advanced: [{
zoom: parseFloat(value)
}]
});
case 'torch':
return track.applyConstraints({
advanced: [{
torch: !!value
}]
});
}
}
},
setState: function (path, value) {
var self = this;

if (typeof self._accessByPath(self.inputMapper, path) === "function") {
value = self._accessByPath(self.inputMapper, path)(value);
}

if (path.startsWith('settings.')) {
var setting = path.substring(9);
return self.applySetting(setting, value);
}
self._accessByPath(self.state, path, value);

console.log(JSON.stringify(self.state));
App.detachListeners();
Quagga.stop();
App.init();
},
inputMapper: {
inputStream: {
constraints: function (value) {
if (/^(\d+)x(\d+)$/.test(value)) {
var values = value.split('x');
return {
width: {
min: parseInt(values[0])
},
height: {
min: parseInt(values[1])
}
};
}
return {
deviceId: value
};
}
},
numOfWorkers: function (value) {
return parseInt(value);
},
decoder: {
readers: function (value) {
if (value === 'ean_extended') {
return [{
format: "ean_reader",
config: {
supplements: [
'ean_5_reader', 'ean_2_reader'
]
}
}];
}
return [{
format: value + "_reader",
config: {}
}];
}
}
},
state: {
inputStream: {
type: "LiveStream",
constraints: {
width: {
min: 640
},
height: {
min: 480
},
aspectRatio: {
min: 1,
max: 100
},
facingMode: "environment" // or user
}
},
locator: {
patchSize: "medium",
halfSample: true
},
numOfWorkers: 2,
frequency: 10,
decoder: {
readers: [{
format: "code_128_reader",
config: {}
}]
},
locate: true
},
lastResult: null
};

App.init();

Quagga.onProcessed(function (result) {
var drawingCtx = Quagga.canvas.ctx.overlay,
drawingCanvas = Quagga.canvas.dom.overlay;

if (result) {
if (result.boxes) {
drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")),
parseInt(drawingCanvas.getAttribute("height")));
result.boxes.filter(function (box) {
return box !== result.box;
}).forEach(function (box) {
Quagga.ImageDebug.drawPath(box, {
x: 0,
y: 1
}, drawingCtx, {
color: "green",
lineWidth: 2
});
});
}

if (result.box) {
Quagga.ImageDebug.drawPath(result.box, {
x: 0,
y: 1
}, drawingCtx, {
color: "#00F",
lineWidth: 2
});
}

if (result.codeResult && result.codeResult.code) {
Quagga.ImageDebug.drawPath(result.line, {
x: 'x',
y: 'y'
}, drawingCtx, {
color: 'red',
lineWidth: 3
});
}
}
});

Quagga.onDetected(function (result) {
var code = result.codeResult.code;
ctrlBarcode.setValue(code); // Put the value in the field <<<-------------------------------------------------------------
var audio = new Audio('MyCode/beep.mp3');
audio.play();
// alert(code);
Quagga.stop();
});
});

Indicates that I have understood and corrected very little of this code. Almost all of it is the result of obtaining the example from the manufacturer's website.

For what you need, you can contact me through my email fernandohumanes@gmail.com

I leave you the example with a copy of the necessary table, so that you install it in your Windows and you can do all the tests you need.

If you are thinking of a system in which you generate the barcode and capture information through it, I think the best solution is to use a QR code.