SVGcode: تطبيق ويب تقدّمي (PWA) لتحويل الصور النقطية إلى رسومات متجه SVG

إنّ SVGcode هو تطبيق ويب تقدّمي يتيح لك تحويل الصور النقطية، مثل JPG أو PNG أو GIF أو WebP أو AVIF وما إلى ذلك إلى رسومات متجهات بتنسيق SVG. وتستخدم واجهة برمجة التطبيقات File System Access API وواجهة برمجة التطبيقات Async Clipboard API وواجهة برمجة تطبيقات معالجة الملفات وتخصيص تراكب عناصر التحكم في النوافذ.

(إذا كنت تفضّل المشاهدة على القراءة، تتوفّر هذه المقالة أيضًا كفيديو.)

من الصور المركّبة إلى الصور المتجهّة

هل سبق لك تكبير صورة وظهرت لك بدرجة تشويش غير مرضية؟ إذا حدث ذلك، من المحتمل أنّك تعاملت مع تنسيق صورة نقطية مثل WebP أو PNG أو JPG.

يؤدي تكبير صورة نقطية إلى ظهورها بشكل مجزّأ.

في المقابل، الرسومات المتجهّة هي صور يتم تحديدها من خلال نقاط في نظام إحداثيات. ويتم ربط هذه النقاط بخطوط ومنحنيات لتكوين مضلّعات وأشكال أخرى. تتمتع الرسومات المتجهّة بميزة مقارنةً بالرسومات النقطية، إذ يمكن تكبيرها أو تصغيرها إلى أي دقة بدون تشويش.

زيادة حجم صورة متجهة بدون فقدان الجودة

نقدّم لك SVGcode

لقد أنشأتُ تطبيق ويب تقدّمي (PWA) يُسمى SVGcode يمكنه مساعدتك في تحويل الصور النقطية إلى متجهات. أُريد أن أُعطي الفضل لمن يستحقه: لم أُخترع هذه الطريقة. باستخدام SVGcode، أعتمد على أداة سطر أوامر تُسمى Potrace من تطوير Peter Selinger والتي حوّلتها إلى Web Assembly، حتى يمكن استخدامها في تطبيق ويب.

لقطة شاشة لتطبيق SVGcode
تطبيق SVGcode

استخدام SVGcode

أولاً، أريد أن أعرض لك كيفية استخدام التطبيق. سأبدأ بالصورة التمهيدية لحدث Chrome Dev Summit التي نزّلتها من قناة ChromiumDev على Twitter. هذه صورة نقطية بتنسيق PNG أسحبها بعد ذلك إلى تطبيق SVGcode. عند إسقاط الملف، يتتبّع التطبيق لون الصورة، إلى أن يظهر إصدار مُوجَّه من الإدخال. يمكنني الآن تكبير الصورة، وكما ترى، تبقى الحواف حادة. ولكن عند تكبير شعار Chrome، يمكنك ملاحظة أنّ عملية التتبُّع لم تكن مثالية، لا سيما أنّ الخطوط العريضة للشعار تبدو مموّهة بعض الشيء. يمكنني تحسين النتيجة من خلال إزالة البقع من عملية التتبُّع عن طريق قمع البقع التي يصل حجمها إلى خمسة بكسل تقريبًا.

يتم تحويل صورة تم إسقاطها إلى ملف SVG.

تأثير الصورة المطبوعة في SVGcode

من الخطوات المهمة لتحويل الصور إلى أشكال هندسية، خاصةً الصور الفوتوغرافية، هي تحويل ملف الصورة إلى صورة ملصق لتقليل عدد الألوان. ويسمح لي SVGcode بإجراء ذلك لكل قناة ألوان، والاطّلاع على رسومات SVG الناتجة أثناء إجراء التغييرات. وعندما أكون راضيًا عن النتيجة، يمكنني حفظ ملف SVG على محرك الأقراص الثابتة واستخدامه في أي مكان أريد.

استخدام ميزة "تحويل الصورة إلى ملصق" لتقليل عدد الألوان

واجهات برمجة التطبيقات المستخدَمة في SVGcode

الآن وبعد أن تعرفت على إمكانات التطبيق، دعوني أريكم بعض واجهات برمجة التطبيقات التي تساعد في تحقيق ذلك.

تطبيق ويب تقدّمي

SVGcode هو تطبيق ويب تقدّمي قابل للتثبيت، وبالتالي يمكن استخدامه بلا اتصال بالإنترنت. يستند التطبيق إلى نموذج Vanilla JS لـ Vite.js ويستخدم المكوّن الإضافي الرائج Vite plugin PWA الذي ينشئ عامل خدمة يستخدم Workbox.js في الخلفية. ‫Workbox هي مجموعة من المكتبات التي يمكنها تشغيل مشغّل خدمات جاهز للنشر في "تطبيقات الويب التقدّمية". قد لا يعمل هذا النمط بالضرورة مع جميع التطبيقات، ولكنّه رائع لحالة استخدام SVGcode.

تراكب عناصر التحكّم في النوافذ

لزيادة المساحة المتوفّرة على الشاشة إلى أقصى حد، يستخدم SVGcode تخصيص Window Controls Overlay من خلال نقل قائمته الرئيسية إلىверху منطقة شريط العناوين. يمكنك الاطّلاع على تفعيل هذا الإعداد في نهاية عملية التثبيت.

تثبيت SVGcode وتفعيل تخصيص "العناصر المركّبة لعناصر التحكّم في النوافذ"

File System Access API

لفتح ملفات الصور المُدخلة وحفظ ملفات SVG الناتجة، أستخدِم File System Access API. يسمح لي هذا بالاحتفاظ بإشارة إلى الملفات التي تم فتحها سابقًا والمتابعة من حيث توقفت، حتى بعد إعادة تحميل التطبيق. عند حفظ أي صورة، يتم تحسينها من خلال مكتبة svgo، وقد تستغرق هذه العملية بعض الوقت، استنادًا إلى مدى تعقيد ملف SVG. يجب استخدام إيماءة المستخدم لعرض مربّع حوار حفظ الملفات. وبالتالي، من المهم الحصول على مؤشر الملف قبل إجراء تحسين SVG (رسومات موجّهة يمكن تغيير حجمها)، كي لا يتم إيقاف إيماءة المستخدم عندما يصبح رسومات SVG المحسّنة جاهزة.

try {
  let svg = svgOutput.innerHTML;
  let handle = null;
  // To not consume the user gesture obtain the handle before preparing the
  // blob, which may take longer.
  if (supported) {
    handle = await showSaveFilePicker({
      types: [{description: 'SVG file', accept: {'image/svg+xml': ['.svg']}}],
    });
  }
  showToast(i18n.t('optimizingSVG'), Infinity);
  svg = await optimizeSVG(svg);
  showToast(i18n.t('savedSVG'));
  const blob = new Blob([svg], {type: 'image/svg+xml'});
  await fileSave(blob, {description: 'SVG file'}, handle);
} catch (err) {
  console.error(err.name, err.message);
  showToast(err.message);
}

السحب والإفلات

لفتح صورة إدخال، يمكنني استخدام ميزة فتح الملف أو، كما رأيت أعلاه، سحب ملف صورة وإفلاته على التطبيق. إنّ ميزة فتح الملف بسيطة جدًا، والأكثر إثارة للاهتمام هو حالة السحب والإفلات. من المزايا الرائعة لهذه الطريقة أنّه يمكنك الحصول على معرّف نظام ملفات من عنصر نقل البيانات من خلال الأسلوب getAsFileSystemHandle(). كما ذكرنا سابقًا، يمكنني الاحتفاظ بهذا الاسم المعرِّف، حتى يكون جاهزًا عند إعادة تحميل التطبيق.

document.addEventListener('drop', async (event) => {
  event.preventDefault();
  dropContainer.classList.remove('dropenter');
  const item = event.dataTransfer.items[0];
  if (item.kind === 'file') {
    inputImage.addEventListener(
      'load',
      () => {
        URL.revokeObjectURL(blobURL);
      },
      {once: true},
    );
    const handle = await item.getAsFileSystemHandle();
    if (handle.kind !== 'file') {
      return;
    }
    const file = await handle.getFile();
    const blobURL = URL.createObjectURL(file);
    inputImage.src = blobURL;
    await set(FILE_HANDLE, handle);
  }
});

لمزيد من التفاصيل، يمكنك الاطّلاع على المقالة حول File System Access API، ومحاولة دراسة رمز المصدر SVGcode في src/js/filesystem.js إذا أردت.

Async Clipboard API

تم أيضًا دمج SVGcode بالكامل مع حافظة نظام التشغيل من خلال Async Clipboard API. يمكنك لصق الصور من مستكشف ملفات نظام التشغيل في التطبيق إما عن طريق النقر على زر لصق الصورة أو عن طريق الضغط على مفتاحَي command أو control بالإضافة إلى v على لوحة المفاتيح.

سيتم لصق صورة من مستكشف الملفات في رمز SVGcode.

أتاحت واجهة برمجة التطبيقات Async Clipboard API مؤخرًا التعامل مع صور SVG أيضًا، وبالتالي يمكنك أيضًا نسخ صورة SVG ولصقها في تطبيق آخر لإجراء مزيد من المعالجة.

نسخ صورة من SVGcode إلى SVGOMG
copyButton.addEventListener('click', async () => {
  let svg = svgOutput.innerHTML;
  showToast(i18n.t('optimizingSVG'), Infinity);
  svg = await optimizeSVG(svg);
  const textBlob = new Blob([svg], {type: 'text/plain'});
  const svgBlob = new Blob([svg], {type: 'image/svg+xml'});
  navigator.clipboard.write([
    new ClipboardItem({
      [svgBlob.type]: svgBlob,
      [textBlob.type]: textBlob,
    }),
  ]);
  showToast(i18n.t('copiedSVG'));
});

لمزيد من المعلومات، يُرجى قراءة مقالة الحافظة غير المتزامنة أو الاطّلاع على الملف src/js/clipboard.js.

معالجة الملفات

من أكثر الميزات التي أفضلها رمز SVGcode هو مدى تكامله مع نظام التشغيل. وبصفتها تطبيقًا مُثبَّتًا من التطبيقات المتوافقة مع تقنية الويب، يمكن أن تصبح معالِجًا للملفات، أو حتى المعالِج التلقائي للملفات، لملفات الصور. ويعني ذلك أنّه عندما أكون في Finder على جهاز macOS، يمكنني النقر بزر الماوس الأيمن على صورة وفتحها باستخدام SVGcode. تُعرف هذه الميزة باسم "معالجة الملفات"، وهي تعمل استنادًا إلى خاصية file_handlers فيملف بيان تطبيق الويب وقائمة انتظار التشغيل، ما يسمح للتطبيق باستخدام الملف الذي تم تمريره.

فتح ملف من الكمبيوتر المكتبي باستخدام تطبيق SVGcode مثبّت.
window.launchQueue.setConsumer(async (launchParams) => {
  if (!launchParams.files.length) {
    return;
  }
  for (const handle of launchParams.files) {
    const file = await handle.getFile();
    if (file.type.startsWith('image/')) {
      const blobURL = URL.createObjectURL(file);
      inputImage.addEventListener(
        'load',
        () => {
          URL.revokeObjectURL(blobURL);
        },
        {once: true},
      );
      inputImage.src = blobURL;
      await set(FILE_HANDLE, handle);
      return;
    }
  }
});

لمزيد من المعلومات، يمكنك الاطّلاع على قسم السماح لتطبيقات الويب المثبَّتة بأن تكون معالِجات الملفات والاطّلاع على رمز المصدر في src/js/filehandling.js.

مشاركة الويب (الملفات)

ومن الأمثلة الأخرى على الدمج مع نظام التشغيل ميزة مشاركة التطبيق. لنفترض أنّني أريد إجراء تعديلات على ملف SVG تم إنشاؤه باستخدام SVGcode، وإحدى طرق التعامل مع ذلك هي حفظ الملف، وتشغيل تطبيق تعديل ملفات SVG، ثم فتح ملف SVG من هناك. ومع ذلك، يمكنك استخدام Web Share API لإجراء عملية مشاركة أكثر سلاسة، إذ تتيح هذه الواجهة مشاركة الملفات مباشرةً. وبالتالي، إذا كان تطبيق تعديل ملفات SVG هو وجهة مشاركة، يمكنه تلقّي الملف مباشرةً بدون أي انحراف.

shareSVGButton.addEventListener('click', async () => {
  let svg = svgOutput.innerHTML;
  svg = await optimizeSVG(svg);
  const suggestedFileName =
    getSuggestedFileName(await get(FILE_HANDLE)) || 'Untitled.svg';
  const file = new File([svg], suggestedFileName, { type: 'image/svg+xml' });
  const data = {
    files: [file],
  };
  if (navigator.canShare(data)) {
    try {
      await navigator.share(data);
    } catch (err) {
      if (err.name !== 'AbortError') {
        console.error(err.name, err.message);
      }
    }
  }
});
تتم مشاركة صورة SVG مع Gmail.

Web Share Target (Files)

في المقابل، يمكن أن يعمل SVGcode أيضًا كهدف مشاركة ويتلقّى الملفات من تطبيقات أخرى. لكي تتمكّن من تنفيذ ذلك، يجب أن يُعلم التطبيق نظام التشغيل عبر Web Share Target API بأنواع البيانات التي يمكنه قبولها. ويحدث ذلك من خلال حقل مخصّص في بيان تطبيق الويب.

{
  "share_target": {
    "action": "https://svgco.de/share-target/",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": {
      "files": [
        {
          "name": "image",
          "accept": ["image/jpeg", "image/png", "image/webp", "image/gif"]
        }
      ]
    }
  }
}

لا يتوفّر مسار action في الواقع، ولكن يتم التعامل معه فقط في fetch معالج عامل الخدمة، الذي ينقل بعد ذلك الملفات المستلَمة للمعالجة الفعلية في التطبيق.

self.addEventListener('fetch', (fetchEvent) => {
  if (
    fetchEvent.request.url.endsWith('/share-target/') &&
    fetchEvent.request.method === 'POST'
  ) {
    return fetchEvent.respondWith(
      (async () => {
        const formData = await fetchEvent.request.formData();
        const image = formData.get('image');
        const keys = await caches.keys();
        const mediaCache = await caches.open(
          keys.filter((key) => key.startsWith('media'))[0],
        );
        await mediaCache.put('shared-image', new Response(image));
        return Response.redirect('./?share-target', 303);
      })(),
    );
  }
});
مشاركة لقطة شاشة مع رمز الرسومات الموجّهة التي يمكن تغيير حجمها (SVG)

الخاتمة

حسنًا، لقد كانت هذه جولة سريعة حول بعض ميزات التطبيق المتقدّمة في SVGcode. نأمل أن يصبح هذا التطبيق أداة أساسية لتلبية احتياجاتك المتعلّقة بمعالجة الصور إلى جانب تطبيقات رائعة أخرى مثل Squoosh أو SVGOMG.

يتوفّر SVGcode على svgco.de. هل ترى ما فعلته هناك؟ يمكنك مراجعة رمز المصدر الخاص به على GitHub. يُرجى العِلم أنّ Potrace مرخّصة من GPL، وكذلك SVGcode. مع أطيب التحيّات، نأمل أن يكون SVGcode مفيدًا، ويُلهمك بعض ميزاته لإنشاء تطبيقك التالي.

الشكر والتقدير

راجع جو ميديل هذه المقالة.