ساخت APK رایگان

API پل جاوااسکریپت

اسکنر QR و بارکد

صفحه‌ی اسکنر Google با یک promise، AppMint.scanCode() - بدون مجوز دوربین، و URL، Wi-Fi، تلفن یا مخاطب تجزیه‌شده برای کدهای QR رایج.

AppMint.scanCode کمک‌کننده#

AppMint.scanCode(options)

نمونه

صفحه‌ی اسکنر کد QR و بارکد Google را باز می‌کند و آنچه اسکن شد را به شما می‌دهد. یک فراخوانی - بدون مجوز دوربین، بدون <video>، بدون رابط کاربری از خودتان.

مقدار بازگشتی: یک Promise که همیشه حل می‌شود: {ok: true, value, displayValue, format, type, …} یا {ok: false, code, message}. نیازمندی: چیزی برای روشن کردن نیست. گوشی به Google Play services نیاز دارد.

هر کد:

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"
}

فقط بعضی قالب‌ها، و گزینه‌ی «خودتان تایپ کنید» برای برچسب‌های آسیب‌دیده:

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);

بخش‌های آماده - فقط بخشی که با r.type می‌خواند وجود دارد:

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);
}

نکته‌ها: نام قالب‌ها: qr_code، aztec، codabar، code_39، code_93، code_128، data_matrix، ean_8، ean_13، itf، pdf417، upc_a، upc_e (نام‌های ناشناخته نادیده گرفته می‌شوند؛ بدون ذکر = همه‌ی قالب‌ها). type یکی از url، wifi، text، phone، email، sms، geo، contact، calendar، isbn، product، driver_license، unknown است. کدهای خطا: cancelled (بسته شد، از جمله با Back)، unavailable (بدون Play services یا اسکنر دانلود نشد)، busy (یک اسکن از قبل باز است)، failed. اولین اسکن روی یک گوشی ممکن است چند ثانیه طول بکشد تا Play services اسکنر را دانلود کند. اپلیکیشن‌های ساخته‌شده با هوش مصنوعی از scanCode() در @/lib/appmintNative استفاده می‌کنند که روی وب هم اسکن می‌کند.

AppMint.textRecognition کمک‌کننده#

AppMint.textRecognition.recognize(image, {script}) / prepare(script, onProgress)

نمونه

متن را در خط‌هایی فراتر از لاتین می‌خواند: دوانگاری (هندی، مراتی، نپالی …)، چینی، ژاپنی و کره‌ای - یا 'auto' که هر خط را امتحان می‌کند و خوانشی را نگه می‌دارد که نویسه‌ی بیشتری دارد. همین گزینه روی تشخیص‌دهنده‌ی استاندارد هم کار می‌کند: new TextDetector({ script: 'devanagari' }). بدون این گزینه همه‌چیز دقیقاً مثل قبل لاتین خوانده می‌شود.

مقدار بازگشتی: recognize() → یک Promise از {script, text, blocks: [{text, box, corners, lines}]} (script = خط استفاده‌شده)؛ با یک DOMException رد می‌شود - NotSupportedError برای خط ناشناخته یا نبودن Google Play services، در غیر این صورت OperationError. prepare() → یک Promise از {script, status: 'ready'}. نیازمندی: Google Play services. تشخیص‌دهنده‌ی هر خط یک دانلود Play services است: خط‌هایی که کدتان نام می‌برد همراه اپلیکیشن نصب می‌شوند، بقیه در اولین استفاده.

متن هندی از یک عکس:

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);
  }
});

ابتدا مدل یک خط را با نمایش پیشرفت دانلود کنید - وگرنه اولین استفاده روی گوشی منتظر دانلود می‌ماند:

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'
});

سندهای ترکیبی - بگذارید اپلیکیشن از میان خط‌هایی که انتظار دارید انتخاب کند:

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 line

نکته‌ها: scripts فهرست AppMint است: latin، devanagari، chinese، japanese، korean. عربی، سیریلیک، تایلندی و عبری تشخیص‌دهنده‌ی روی دستگاه ندارند. WebToApk.__ocrRecognizeWith(requestId, base64, optionsJson) حمل داخلی است؛ از کمکی استفاده کنید.

__ocrAvailable پل#

window.WebToApk.__ocrAvailable(): Boolean

TextDetector / recognizeText(): Google Play services اینجاست (خانه‌ی ماژول OCR).

نمونه

حمل داخلی پشت TextDetector: اینکه Google Play services (جایی که تشخیص‌دهنده از آن می‌آید) روی این گوشی هست یا نه. صفحه‌ها از API استاندارد استفاده می‌کنند؛ هرگز مستقیم صدایش نزنید - متدهای __ ممکن است بدون اطلاع تغییر کنند.

مقدار بازگشتی: true یا false. وقتی false است، TextDetector اصلاً نصب نیست. نیازمندی: هیچ.

از API عمومی استفاده کنید:

if ('TextDetector' in window) scanButton.hidden = false;

یک بار هنگام شروع بین جریان اسکن و فرم تایپی انتخاب کنید. وقتی گوشی Google Play services ندارد تشخیص‌دهنده ساده‌تر وجود ندارد، پس صفحه باید راه دیگری ارائه دهد:

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);

تشخیص‌دهنده‌ای که وجود دارد هم می‌تواند در لحظه‌ی فراخوانی بگوید تشخیص فعلاً در دسترس نیست. این دو خطا را با نام خطا از هم جدا کنید:

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;
  }
}

نکته‌ها: در پوسته‌ی اپلیکیشن پیاده‌سازی شده است (TextRecognitionBridge.kt).

__ocrRecognize پل#

window.WebToApk.__ocrRecognize(requestId: String, base64: String)

متن را در [base64] (PNG/JPEG) تشخیص می‌دهد؛ پاسخ به‌صورت `appmint:ocr {requestId, blocks|error}` می‌رسد.

نمونه

حمل داخلی پشت TextDetector.detect(): متن را در یک PNG/JPEG تشخیص می‌دهد. صفحه‌ها از API استاندارد استفاده می‌کنند؛ هرگز مستقیم صدایش نزنید - متدهای __ ممکن است بدون اطلاع تغییر کنند.

مقدار بازگشتی: چیزی؛ پاسخ به‌صورت رویداد appmint:ocr با {requestId, blocks:[{text, box, corners, lines}]} یا {requestId, error} (unavailable، bad-image، failed: …) می‌رسد. نیازمندی: هیچ.

از API عمومی استفاده کنید:

const lines = await new TextDetector().detect(document.getElementById('receipt'));
console.log(lines.map((l) => l.rawValue));

متن عکسی را که کاربر انتخاب می‌کند بخوانید. detect() علاوه بر <img>، <canvas> یا Blob/File هم می‌پذیرد 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>

دور هر سطر شناسایی‌شده را روی تصویر قاب بکشید. boundingBox بر حسب پیکسل‌های خود تصویر است، پس آن را به اندازه‌ی نمایش‌داده‌شده مقیاس دهید. تصویر باید بارگذاری‌اش تمام شده باشد، وگرنه detect() رد می‌شود با 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);
  });
}

یک مبلغ را از رسید بیرون بکشید. تصویر بین‌مبدأ که بوم را آلوده کند با SecurityError رد می‌شود، پس تصویر را از مبدأ خود اپلیکیشن ارائه دهید:

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;
  }
}

نکته‌ها: در پوسته‌ی اپلیکیشن پیاده‌سازی شده است (TextRecognitionBridge.kt).

__ocrRecognizeWith پل#

window.WebToApk.__ocrRecognizeWith(requestId: String, base64: String, optionsJson: String)

[__ocrRecognize] با گزینه‌های [optionsJson] `{script: 'latin'|'devanagari'|'chinese'|'japanese'|'korean'|'auto', scripts?}`؛ پاسخ `appmint:ocr` نام `script` استفاده‌شده را هم می‌آورد.

نمونه

همراه با AppMint.textRecognition مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

متن را در خط‌هایی فراتر از لاتین می‌خواند: دوانگاری (هندی، مراتی، نپالی …)، چینی، ژاپنی و کره‌ای - یا 'auto' که هر خط را امتحان می‌کند و خوانشی را نگه می‌دارد که نویسه‌ی بیشتری دارد. همین گزینه روی تشخیص‌دهنده‌ی استاندارد هم کار می‌کند: new TextDetector({ script: 'devanagari' }). بدون این گزینه همه‌چیز دقیقاً مثل قبل لاتین خوانده می‌شود.

مقدار بازگشتی: recognize() → یک Promise از {script, text, blocks: [{text, box, corners, lines}]} (script = خط استفاده‌شده)؛ با یک DOMException رد می‌شود - NotSupportedError برای خط ناشناخته یا نبودن Google Play services، در غیر این صورت OperationError. prepare() → یک Promise از {script, status: 'ready'}. نیازمندی: Google Play services. تشخیص‌دهنده‌ی هر خط یک دانلود Play services است: خط‌هایی که کدتان نام می‌برد همراه اپلیکیشن نصب می‌شوند، بقیه در اولین استفاده.

متن هندی از یک عکس:

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);
  }
});

ابتدا مدل یک خط را با نمایش پیشرفت دانلود کنید - وگرنه اولین استفاده روی گوشی منتظر دانلود می‌ماند:

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'
});

سندهای ترکیبی - بگذارید اپلیکیشن از میان خط‌هایی که انتظار دارید انتخاب کند:

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 line

نکته‌ها: scripts فهرست AppMint است: latin، devanagari، chinese، japanese، korean. عربی، سیریلیک، تایلندی و عبری تشخیص‌دهنده‌ی روی دستگاه ندارند. WebToApk.__ocrRecognizeWith(requestId, base64, optionsJson) حمل داخلی است؛ از کمکی استفاده کنید.

prepareTextRecognition پل#

window.WebToApk.prepareTextRecognition(script: String): String

تشخیص‌دهنده‌ی متن را برای [script] از پیش دانلود می‌کند. «checking» برمی‌گرداند (سپس `appmint:ocr-model {script, status: 'downloading'|'ready'|'failed', progress?}`)، «unsupported-script» یا «unavailable» (بدون Google Play services).

نمونه

تشخیص‌دهنده‌ی متن روی دستگاه را برای یک خط از پیش دانلود می‌کند تا اولین recognize() منتظر نماند. صفحه‌ها معمولاً AppMint.textRecognition.prepare(script) را صدا می‌زنند که این را در بر می‌گیرد.

مقدار بازگشتی: یک رشته بلافاصله - "checking" (نتیجه بعداً به‌صورت رویدادهای appmint:ocr-model {script, status: 'downloading' | 'ready' | 'failed', progress?, error?} می‌آید)، "unsupported-script" یا "unavailable" (بدون Google Play services). 'auto' همه‌ی خطوط را آماده می‌کند. نیازمندی: 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);
}

آماده کنید، سپس از طریق پوشاننده اسکن کنید:

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;
  }
}

خط‌های پشتیبانی‌نشده و نبودن Play services در مقدار بازگشتی گزارش می‌شوند:

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.');
}

نکته‌ها: خط‌ها: latin، devanagari، chinese، japanese، korean، auto. خطی که از قبل روی گوشی است بلافاصله ready جواب می‌دهد.

scanCode پل#

window.WebToApk.scanCode(optionsJson: String, callbackId: String)

اسکنر کد Google را باز می‌کند؛ [optionsJson] `{formats?, autoZoom?, manualInput?}`. promise AppMint.scanCode با [callbackId] را حل می‌کند.

نمونه

زیرساخت داخلی پشت AppMint.scanCode() - صفحه‌ها از همان کمکی Promise استفاده می‌کنند که اسکنر QR/بارکد Google را باز می‌کند و با نتیجه حل می‌شود.

مقدار بازگشتی: مستقیماً چیزی نه؛ نتیجه promise مربوط به AppMint.scanCode() را حل می‌کند. نیازمندی: چیزی برای روشن کردن نیست (بدون مجوز دوربین). گوشی به Google Play services نیاز دارد.

API عمومی:

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 + ')');
}

از روی یک کد QR چاپی به یک شبکه‌ی Wi-Fi وصل شوید. کد Wi-Fi با type: 'wifi' و یک شیء 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'
}

محصولی را با بارکد فروشگاهی‌اش جست‌وجو کنید و وقتی برچسب آسیب دیده به کاربر اجازه دهید ارقام را تایپ کند (manualInput). اولین اسکن روی یک گوشی ممکن است اسکنر را دانلود کند که در حالت آفلاین با unavailable شکست می‌خورد:

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 + ')');
}

هر چیزی را که یک کد QR به آن اشاره می‌کند باز کنید، با رمزگشایی‌شده‌اش 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);
}

نکته‌ها: متد خام optionsJson ({formats?, autoZoom?, manualInput?} به‌صورت رشته‌ی JSON) و یک callbackId می‌گیرد؛ پاسخ به جدول پاسخ داخلی کمکی تحویل می‌شود، پس صفحه‌ای که مستقیم صدایش بزند پاسخی نمی‌گیرد. هر بار فقط یک اسکن اجرا می‌شود - فراخوانی دوم code: 'busy' جواب می‌دهد. برای هر فیلد نتیجه و کد خطا AppMint.scanCode را ببینید.