API du pont JavaScript
Scanner de QR codes et de codes-barres
L'écran de scan de Google à partir d'une seule promesse, AppMint.scanCode() : sans autorisation d'appareil photo, avec l'URL, le Wi-Fi, le téléphone ou le contact analysés pour les QR codes courants.
AppMint.scanCode assistant#
AppMint.scanCode(options)
Exemple
Ouvre l'écran de scan de QR codes et de codes-barres de Google et vous donne ce qui a été scanné. Un seul appel : sans autorisation d'appareil photo, sans <video>, sans interface à créer.
Renvoie : une Promise qui se résout toujours : {ok: true, value, displayValue, format, type, …} ou {ok: false, code, message}. Nécessite : rien à activer. Le téléphone doit disposer de Google Play services.
N'importe quel code :
async function scan() {
if (!(window.AppMint && AppMint.scanCode)) { alert('Open this page in the app to scan'); return; }
var r = await AppMint.scanCode();
if (!r.ok) {
if (r.code === 'cancelled') return; // the user closed the scanner
if (r.code === 'unavailable') alert('The scanner is not available on this phone');
else if (r.code === 'busy') alert('A scan is already open');
else alert('Scan failed: ' + r.message); // 'failed'
return;
}
console.log(r.value, r.format, r.type); // "TICKET-4821", "qr_code", "text"
}Seulement certains formats, et une option « saisir à la main » pour les étiquettes abîmées :
var r = await AppMint.scanCode({
formats: ['ean_13', 'ean_8', 'upc_a', 'upc_e'], // shop barcodes
manualInput: true, // lets the user type the code
autoZoom: true // default true
});
if (r.ok) lookUpProduct(r.value);Éléments prêts à l'emploi : seul l'élément correspondant à r.type est présent :
var r = await AppMint.scanCode({ formats: ['qr_code'] });
if (r.ok) {
if (r.type === 'url') openLink(r.url.url); // url: { url, title }
if (r.type === 'wifi') showWifi(r.wifi.ssid, r.wifi.password, r.wifi.encryption); // 'wpa'|'wep'|'open'
if (r.type === 'phone') callNumber(r.phone); // a string
if (r.type === 'email') writeMail(r.email.address, r.email.subject, r.email.body);
if (r.type === 'sms') writeSms(r.sms.phoneNumber, r.sms.message);
if (r.type === 'geo') showMap(r.geo.lat, r.geo.lng);
if (r.type === 'contact') addContact(r.contact.name, r.contact.organization, r.contact.phones, r.contact.emails);
}Remarques : noms de formats : qr_code, aztec, codabar, code_39, code_93, code_128, data_matrix, ean_8, ean_13, itf, pdf417, upc_a, upc_e (les noms inconnus sont ignorés ; aucun = tous les formats). type est l'un de url, wifi, text, phone, email, sms, geo, contact, calendar, isbn, product, driver_license, unknown. Codes d'erreur : cancelled (fermé, y compris avec Retour), unavailable (pas de Play services, ou le scanner n'a pas pu être téléchargé), busy (un scan est déjà ouvert), failed. Le premier scan sur un téléphone peut prendre quelques secondes, le temps que Play services télécharge le scanner. Les applications créées par IA utilisent scanCode() de @/lib/appmintNative, qui scanne aussi sur le web.
AppMint.textRecognition assistant#
AppMint.textRecognition.recognize(image, {script}) / prepare(script, onProgress)
Exemple
Lit du texte dans des écritures autres que le latin : devanagari (hindi, marathi, népalais …), chinois, japonais et coréen, ou 'auto', qui essaie chaque écriture et retient la lecture comptant le plus de caractères. La même option fonctionne sur le détecteur standard : new TextDetector({ script: 'devanagari' }). Sans l'option, tout se lit en latin exactement comme avant.
Renvoie : recognize() → une Promise de {script, text, blocks: [{text, box, corners, lines}]} (script = celle utilisée) ; elle est rejetée avec une DOMException : NotSupportedError pour une écriture inconnue ou l'absence de Google Play services, OperationError sinon. prepare() → une Promise de {script, status: 'ready'}. Nécessite : Google Play services. Le module de reconnaissance de chaque écriture est un téléchargement Play services : les écritures nommées dans votre code sont installées avec l'application, les autres à la première utilisation.
Texte hindi à partir d'une photo :
document.getElementById('photo').addEventListener('change', async function (e) {
var file = e.target.files[0];
if (!file || !(window.AppMint && AppMint.textRecognition)) return;
try {
var r = await AppMint.textRecognition.recognize(file, { script: 'devanagari' });
document.getElementById('out').textContent = r.text;
} catch (err) {
alert('Could not read the photo: ' + err.name);
}
});Téléchargez d'abord le modèle d'une écriture, avec la progression : sinon, la première utilisation sur un téléphone attend le téléchargement :
async function getReady() {
var bar = document.getElementById('model-progress');
await AppMint.textRecognition.prepare('japanese', function (s) {
if (s.progress != null) bar.value = s.progress; // 0..1 while 'downloading'
});
bar.hidden = true;
}
// The same states arrive as events, also when a recognize() call starts a download:
window.addEventListener('appmint:ocr-model', function (e) {
console.log(e.detail.script, e.detail.status, e.detail.progress); // 'downloading' | 'ready' | 'failed'
});Documents mixtes : laissez l'application choisir parmi les écritures que vous attendez :
var r = await AppMint.textRecognition.recognize(img, { script: 'auto', scripts: ['latin', 'chinese', 'korean'] });
console.log('read as ' + r.script + ': ' + r.text);
var lines = await new TextDetector({ script: 'korean' }).detect(img); // standard shape, one entry per lineRemarques : scripts est la liste d'AppMint : latin, devanagari, chinese, japanese, korean. L'arabe, le cyrillique, le thaï et l'hébreu n'ont pas de module de reconnaissance sur l'appareil. WebToApk.__ocrRecognizeWith(requestId, base64, optionsJson) est le transport interne ; utilisez la fonction d'aide.
__ocrAvailable pont#
window.WebToApk.__ocrAvailable(): Boolean
TextDetector / recognizeText() : Google Play services est présent (le foyer du module OCR).
Exemple
Transport interne derrière TextDetector : indique si Google Play services (d'où provient le module de reconnaissance) est présent sur ce téléphone. Les pages utilisent l'API standard ; n'appelez jamais ceci directement : les méthodes __ peuvent changer sans préavis.
Renvoie : true ou false. Lorsque false, TextDetector n'est pas du tout installé. Nécessite : rien.
Utilisez l'API publique :
if ('TextDetector' in window) scanButton.hidden = false;Choisissez une fois, au démarrage, entre le flux de scan et un formulaire à saisir. Lorsque le téléphone n'a pas Google Play services, le détecteur est simplement absent : la page doit donc proposer une autre voie d'accès :
function setUpReceiptEntry() {
var canScan = typeof window.TextDetector === 'function';
document.getElementById('scan-receipt').hidden = !canScan;
document.getElementById('type-receipt').hidden = canScan;
if (!canScan) document.getElementById('entry-hint').textContent =
'Text scanning is not available on this phone. Type the total below.';
}
document.addEventListener('DOMContentLoaded', setUpReceiptEntry);Un détecteur qui existe peut tout de même indiquer que la reconnaissance est indisponible au moment de l'appel. Distinguez les deux échecs par le nom de l'erreur :
async function readLabel(img) {
if (typeof window.TextDetector !== 'function') return null;
try {
var lines = await new TextDetector().detect(img);
return lines.map(function (l) { return l.rawValue; }).join('\n');
} catch (e) {
if (e.name === 'NotSupportedError') showMessage('Text recognition is not available right now.');
else showMessage('Could not read the label: ' + e.message);
return null;
}
}Remarques : implémenté dans la coque de l'application (TextRecognitionBridge.kt).
__ocrRecognize pont#
window.WebToApk.__ocrRecognize(requestId: String, base64: String)
Reconnaît le texte dans [base64] (PNG/JPEG) ; la réponse arrive sous la forme `appmint:ocr {requestId, blocks|error}`.
Exemple
Transport interne derrière TextDetector.detect() : reconnaît le texte d'un PNG/JPEG. Les pages utilisent l'API standard ; n'appelez jamais ceci directement : les méthodes __ peuvent changer sans préavis.
Renvoie : rien ; la réponse arrive sous la forme de l'événement appmint:ocr {requestId, blocks:[{text, box, corners, lines}]} ou {requestId, error} (unavailable, bad-image, failed: …). Nécessite : rien.
Utilisez l'API publique :
const lines = await new TextDetector().detect(document.getElementById('receipt'));
console.log(lines.map((l) => l.rawValue));Lisez le texte d'une photo choisie par l'utilisateur. detect() accepte un Blob/File ainsi qu'un <img>, <canvas> ou ImageData:
<input type="file" id="doc" accept="image/*" capture="environment">
<pre id="out"></pre>
<script>
document.getElementById('doc').addEventListener('change', async function (e) {
var file = e.target.files[0];
if (!file || typeof window.TextDetector !== 'function') return;
try {
var lines = await new TextDetector().detect(file);
document.getElementById('out').textContent = lines.map(function (l) { return l.rawValue; }).join('\n');
} catch (err) {
document.getElementById('out').textContent = 'Could not read the photo (' + err.name + ')';
}
});
</script>Entourez d'un contour chaque ligne reconnue sur l'image. boundingBox est exprimé dans les pixels propres de l'image : adaptez-le donc à la taille affichée. L'image doit avoir fini de se charger, sinon detect() est rejeté avec InvalidStateError:
async function highlightLines(img, overlay) {
if (!img.complete) await new Promise(function (r) { img.onload = r; });
var lines = await new TextDetector().detect(img);
var sx = img.clientWidth / img.naturalWidth, sy = img.clientHeight / img.naturalHeight;
overlay.innerHTML = '';
lines.forEach(function (l) {
var b = l.boundingBox, box = document.createElement('div');
box.className = 'ocr-box';
box.title = l.rawValue;
box.style.cssText = 'position:absolute;left:' + b.x * sx + 'px;top:' + b.y * sy +
'px;width:' + b.width * sx + 'px;height:' + b.height * sy + 'px';
overlay.appendChild(box);
});
}Extrayez un montant d'un reçu. Une image d'une autre origine qui contamine le canvas est rejetée avec SecurityError : servez donc l'image depuis l'origine propre de l'application :
async function findTotal(img) {
try {
var lines = await new TextDetector().detect(img);
var hit = lines.find(function (l) { return /total/i.test(l.rawValue); });
var m = hit && hit.rawValue.match(/(\d+[.,]\d{2})/);
return m ? parseFloat(m[1].replace(',', '.')) : null;
} catch (e) {
if (e.name === 'SecurityError') console.warn('Image is from another origin; load it from the app instead.');
else console.warn('OCR failed:', e.name, e.message);
return null;
}
}Remarques : implémenté dans la coque de l'application (TextRecognitionBridge.kt).
__ocrRecognizeWith pont#
window.WebToApk.__ocrRecognizeWith(requestId: String, base64: String, optionsJson: String)
[__ocrRecognize] avec les options [optionsJson] `{script: 'latin'|'devanagari'|'chinese'|'japanese'|'korean'|'auto', scripts?}` ; la réponse `appmint:ocr` indique aussi le `script` utilisé.
Exemple
Documenté avec AppMint.textRecognition : l'exemple qui s'y trouve illustre aussi celui-ci.
Lit du texte dans des écritures autres que le latin : devanagari (hindi, marathi, népalais …), chinois, japonais et coréen, ou 'auto', qui essaie chaque écriture et retient la lecture comptant le plus de caractères. La même option fonctionne sur le détecteur standard : new TextDetector({ script: 'devanagari' }). Sans l'option, tout se lit en latin exactement comme avant.
Renvoie : recognize() → une Promise de {script, text, blocks: [{text, box, corners, lines}]} (script = celle utilisée) ; elle est rejetée avec une DOMException : NotSupportedError pour une écriture inconnue ou l'absence de Google Play services, OperationError sinon. prepare() → une Promise de {script, status: 'ready'}. Nécessite : Google Play services. Le module de reconnaissance de chaque écriture est un téléchargement Play services : les écritures nommées dans votre code sont installées avec l'application, les autres à la première utilisation.
Texte hindi à partir d'une photo :
document.getElementById('photo').addEventListener('change', async function (e) {
var file = e.target.files[0];
if (!file || !(window.AppMint && AppMint.textRecognition)) return;
try {
var r = await AppMint.textRecognition.recognize(file, { script: 'devanagari' });
document.getElementById('out').textContent = r.text;
} catch (err) {
alert('Could not read the photo: ' + err.name);
}
});Téléchargez d'abord le modèle d'une écriture, avec la progression : sinon, la première utilisation sur un téléphone attend le téléchargement :
async function getReady() {
var bar = document.getElementById('model-progress');
await AppMint.textRecognition.prepare('japanese', function (s) {
if (s.progress != null) bar.value = s.progress; // 0..1 while 'downloading'
});
bar.hidden = true;
}
// The same states arrive as events, also when a recognize() call starts a download:
window.addEventListener('appmint:ocr-model', function (e) {
console.log(e.detail.script, e.detail.status, e.detail.progress); // 'downloading' | 'ready' | 'failed'
});Documents mixtes : laissez l'application choisir parmi les écritures que vous attendez :
var r = await AppMint.textRecognition.recognize(img, { script: 'auto', scripts: ['latin', 'chinese', 'korean'] });
console.log('read as ' + r.script + ': ' + r.text);
var lines = await new TextDetector({ script: 'korean' }).detect(img); // standard shape, one entry per lineRemarques : scripts est la liste d'AppMint : latin, devanagari, chinese, japanese, korean. L'arabe, le cyrillique, le thaï et l'hébreu n'ont pas de module de reconnaissance sur l'appareil. WebToApk.__ocrRecognizeWith(requestId, base64, optionsJson) est le transport interne ; utilisez la fonction d'aide.
prepareTextRecognition pont#
window.WebToApk.prepareTextRecognition(script: String): String
Télécharge à l'avance le module de reconnaissance de texte pour [script]. Renvoie "checking" (puis `appmint:ocr-model {script, status: 'downloading'|'ready'|'failed', progress?}`), "unsupported-script" ou "unavailable" (pas de Google Play services).
Exemple
Télécharge à l'avance le module de reconnaissance de texte sur l'appareil pour une écriture, afin que le premier recognize() n'attende pas. Les pages appellent normalement AppMint.textRecognition.prepare(script), qui l'encapsule.
Renvoie : une chaîne immédiatement : "checking" (le résultat suit sous forme d'événements appmint:ocr-model {script, status: 'downloading' | 'ready' | 'failed', progress?, error?}), "unsupported-script" ou "unavailable" (pas de Google Play services). 'auto' prépare tous les scripts. Nécessite : Google Play services.
if (window.WebToApk && typeof window.WebToApk.prepareTextRecognition === 'function') {
window.addEventListener('appmint:ocr-model', function (e) {
if (e.detail.script !== 'chinese') return;
if (e.detail.status === 'downloading') console.log('downloading', Math.round((e.detail.progress || 0) * 100) + '%');
if (e.detail.status === 'ready') document.getElementById('scan').disabled = false;
if (e.detail.status === 'failed') console.warn(e.detail.error);
});
var state = window.WebToApk.prepareTextRecognition('chinese');
if (state !== 'checking') console.warn('Chinese text recognition: ' + state);
}Préparez, puis scannez via l'encapsulation :
async function readSign(canvas) {
if (!(window.AppMint && AppMint.textRecognition)) return null;
try {
await AppMint.textRecognition.prepare('japanese');
return await AppMint.textRecognition.recognize(canvas, { script: 'japanese' });
} catch (err) {
console.warn('Text recognition:', err.name, err.message); // e.g. NotSupportedError without Play services
return null;
}
}Les écritures non prises en charge et l'absence de Play services sont signalées dans la valeur de retour :
const W = window.WebToApk;
if (W && typeof W.prepareTextRecognition === 'function') {
const state = W.prepareTextRecognition('arabic');
if (state === 'unsupported-script') showMessage('Arabic text cannot be read on the phone.');
else if (state === 'unavailable') showMessage('Text recognition needs Google Play services.');
}Remarques : écritures : latin, devanagari, chinese, japanese, korean, auto. Une écriture déjà présente sur le téléphone répond ready immédiatement.
scanCode pont#
window.WebToApk.scanCode(optionsJson: String, callbackId: String)
Ouvre le scanner de codes de Google ; [optionsJson] `{formats?, autoZoom?, manualInput?}`. Résout la promesse AppMint.scanCode [callbackId].
Exemple
Mécanisme interne derrière AppMint.scanCode() : les pages utilisent cette fonction d'aide à base de Promise, qui ouvre le scanner de QR codes et de codes-barres de Google et renvoie le résultat.
Renvoie : rien directement ; le résultat résout la promesse AppMint.scanCode(). Nécessite : rien à activer (pas d'autorisation d'appareil photo). Le téléphone doit disposer de Google Play services.
L'API publique :
async function scanTicket() {
if (!(window.AppMint && AppMint.scanCode)) { alert('Open this page in the app to scan'); return; }
var r = await AppMint.scanCode({ formats: ['qr_code'] });
if (r.ok) checkTicket(r.value); // r.format, r.type, r.displayValue…
else if (r.code !== 'cancelled') alert('Scan failed (' + r.code + ')');
}Rejoignez un réseau Wi-Fi à partir d'un QR code imprimé. Un code Wi-Fi revient avec type: 'wifi' et un objet wifi :
async function scanWifi() {
if (!(window.AppMint && AppMint.scanCode)) return;
var r = await AppMint.scanCode({ formats: ['qr_code'] });
if (!r.ok) { if (r.code !== 'cancelled') alert('Scan failed (' + r.code + ')'); return; }
if (r.type !== 'wifi') { alert('That is not a Wi-Fi code.'); return; }
showWifiCard(r.wifi.ssid, r.wifi.password, r.wifi.encryption); // encryption: 'wpa' | 'wep' | 'open'
}Recherchez un produit par son code-barres de magasin, en laissant l'utilisateur saisir les chiffres lorsque l'étiquette est abîmée (manualInput). Le premier scan sur un téléphone peut télécharger le scanner, ce qui échoue avec unavailable hors ligne :
async function scanProduct() {
if (!(window.AppMint && AppMint.scanCode)) { showManualEntry(); return; }
var r = await AppMint.scanCode({ formats: ['ean_13', 'ean_8', 'upc_a', 'upc_e'], manualInput: true });
if (r.ok) return lookUpProduct(r.value, r.format); // e.g. '4006381333931', 'ean_13'
if (r.code === 'busy') return; // a scan is already open
if (r.code === 'unavailable') alert('The scanner is not available: ' + r.message);
else if (r.code !== 'cancelled') alert('Scan failed (' + r.code + ')');
}Ouvrez ce que pointe un QR code, grâce à son contenu décodé type:
async function scanAndOpen() {
var r = await AppMint.scanCode({ formats: ['qr_code'], autoZoom: true });
if (!r.ok) return;
if (r.type === 'url') location.href = r.url.url;
else if (r.type === 'phone') location.href = 'tel:' + r.phone;
else if (r.type === 'email') location.href = 'mailto:' + r.email.address;
else if (r.type === 'geo') showOnMap(r.geo.lat, r.geo.lng);
else if (r.type === 'contact') saveContact(r.contact.name, r.contact.phones, r.contact.emails);
else showText(r.displayValue);
}Remarques : la méthode brute prend optionsJson ({formats?, autoZoom?, manualInput?} sous forme de chaîne JSON) et un callbackId ; la réponse est livrée dans la table de réponses interne de la fonction d'aide, donc une page qui l'appelle directement n'obtient aucune réponse. Un seul scan à la fois : un deuxième appel répond code: 'busy'. Voir AppMint.scanCode pour chaque champ de résultat et chaque code d'erreur.