أنشئ APK مجانًا

واجهة JavaScript bridge API

ماسح QR والباركود

شاشة ماسح Google من وعد واحد، AppMint.scanCode() - بلا إذن كاميرا، مع الرابط أو الواي فاي أو الهاتف أو جهة الاتصال المحلَّلة لرموز QR الشائعة.

AppMint.scanCode دالة مساعدة (helper)#

AppMint.scanCode(options)

مثال

يفتح شاشة ماسح رموز QR والباركود من Google ويعطيك ما تم مسحه. استدعاء واحد - بلا إذن كاميرا، بلا <video>، بلا واجهة خاصة بك.

تُرجع: Promise يُحلّ دائمًا: {ok: true, value, displayValue, format, type, …} أو {ok: false, code, message}. المتطلبات: لا شيء لتفعيله. يحتاج الهاتف إلى خدمات Google Play.

أي كود:

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 (أُغلق، بما في ذلك بزر الرجوع)، unavailable (لا خدمات Play، أو تعذّر تنزيل الماسح)، busy (مسح مفتوح بالفعل)، failed. قد يستغرق أول مسح على الهاتف بضع ثوانٍ بينما تنزّل خدمات Play الماسح. التطبيقات المبنية بالذكاء الاصطناعي تستخدم scanCode() من @/lib/appmintNative، الذي يمسح أيضًا على الويب.

AppMint.textRecognition دالة مساعدة (helper)#

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، وOperationError فيما عدا ذلك. prepare() → Promise لـ {script, status: 'ready'}. المتطلبات: خدمات Google Play. قارئ كل نظام كتابي تنزيل من خدمات Play: الأنظمة التي يسمّيها كودك تُثبَّت مع التطبيق، وغيرها عند أول استخدام.

نص هندي من صورة:

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 الجسر (bridge)#

window.WebToApk.__ocrAvailable(): Boolean

TextDetector / recognizeText(): خدمات Google Play موجودة هنا (موطن وحدة OCR).

مثال

ناقل داخلي وراء TextDetector: هل خدمات Google Play (مصدر القارئ) موجودة على هذا الهاتف. تستخدم الصفحات الواجهة القياسية؛ لا تستدعِ هذا مباشرة أبدًا - قد تتغير طرق __ دون إشعار.

تُرجع: true أو false. عندما تكون false، لا يكون TextDetector مثبّتًا إطلاقًا. المتطلبات: لا شيء.

استخدم الواجهة العامة:

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

اختر بين مسار المسح ونموذج الكتابة مرة واحدة عند بدء التشغيل. عندما لا يكون في الهاتف خدمات Google Play يكون الكاشف غائبًا ببساطة، لذا يجب أن تقدّم الصفحة طريقة أخرى للدخول:

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 الجسر (bridge)#

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

يتعرف على النص في [base64] (PNG/JPEG)؛ يصل الرد كـ `appmint:ocr {requestId, blocks|error}`.

مثال

ناقل داخلي وراء TextDetector.detect(): يتعرف على النص في PNG/JPEG. تستخدم الصفحات الواجهة القياسية؛ لا تستدعِ هذا مباشرة أبدًا - قد تتغير طرق __ دون إشعار.

تُرجع: لا شيء؛ يصل الرد كحدث appmint:ocr {requestId, blocks:[{text, box, corners, lines}]} أو {requestId, error} (unavailable، bad-image، failed: …). المتطلبات: لا شيء.

استخدم الواجهة العامة:

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

اقرأ نص صورة يختارها المستخدم. يقبل detect() كائن Blob/File إضافة إلى <img> أو <canvas> أو 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);
  });
}

استخرج مبلغًا من إيصال. صورة من أصل مختلف تلوّث اللوحة (canvas) تُرفض مع 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 الجسر (bridge)#

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، وOperationError فيما عدا ذلك. prepare() → Promise لـ {script, status: 'ready'}. المتطلبات: خدمات Google Play. قارئ كل نظام كتابي تنزيل من خدمات Play: الأنظمة التي يسمّيها كودك تُثبَّت مع التطبيق، وغيرها عند أول استخدام.

نص هندي من صورة:

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 الجسر (bridge)#

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

ينزّل قارئ النصوص للنظام الكتابي [script] قبل الاستخدام. يُرجع "checking" (ثم `appmint:ocr-model {script, status: 'downloading'|'ready'|'failed', progress?}`)، أو "unsupported-script" أو "unavailable" (لا توجد خدمات Google Play).

مثال

ينزّل قارئ النصوص على الجهاز لنظام كتابي واحد قبل الاستخدام، فلا ينتظر أول recognize(). تستدعي الصفحات عادةً AppMint.textRecognition.prepare(script) الذي يغلّف هذا.

تُرجع: سلسلة فورًا - "checking" (تتبعها النتيجة كأحداث appmint:ocr-model {script, status: 'downloading' | 'ready' | 'failed', progress?, error?})، "unsupported-script" أو "unavailable" (لا توجد خدمات Google Play). 'auto' يجهّز كل الأنظمة الكتابية. المتطلبات: خدمات Google Play.

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 في القيمة المُرجَعة:

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 الجسر (bridge)#

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

يفتح ماسح الرموز من Google؛ [optionsJson] `{formats?, autoZoom?, manualInput?}`. يُحلّ وعد AppMint.scanCode [callbackId].

مثال

آلية داخلية وراء AppMint.scanCode() - تستخدم الصفحات دالة Promise تلك، التي تفتح ماسح QR/الباركود من Google وتُرجع النتيجة.

تُرجع: لا شيء مباشرة؛ تُحلّ النتيجة وعد AppMint.scanCode(). المتطلبات: لا شيء لتفعيله (بلا إذن كاميرا). يحتاج الهاتف إلى خدمات Google Play.

الواجهة العامة:

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 مطبوع. يعود رمز الواي فاي مع 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 لكل حقول النتيجة ورموز الخطأ.

يتم إنشاؤه من بيئة تشغيل التطبيق وملفات الأمثلة الخاصة به عند كل بناء للمستندات. اقرأه بصيغة Markdown · جميع العائلات.

تمت المراجعة مقابل الجسر المُرفق بتاريخ 2026-10-22.