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

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

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

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

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

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

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

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

نقدّم لك SVGcode

لقد أنشأتُ تطبيقًا متوافقًا مع الأجهزة الجوّالة يُسمى 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.

Window Controls Overlay

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

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

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);
      })(),
    );
  }
});
مشاركة لقطة شاشة مع SVGcode

الخاتمة

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

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

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

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