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

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

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

من خط نقطي إلى متجه

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

يؤدي تكبير الصورة النقطية إلى أن تبدو متقطّعة.

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

تكبير صورة الخط المتجه بدون فقدان الجودة.

إضافة رمز SVG

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

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

استخدام رمز SVG

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

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

النشر باستخدام رمز SVG

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

جارٍ لصق صورة لتقليل عدد الألوان.

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

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

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

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

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

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

تثبيت رمز SVG وتفعيل تخصيص عناصر Window Controls Overlay.

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 في لوحة المفاتيح.

لصق صورة من مستكشف الملفات إلى رمز SVG.

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

جارٍ نسخ صورة من رمز SVG إلى تنسيق SVG OMG.
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.

التعامل مع الملفات

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

فتح ملف من جهاز كمبيوتر مكتبي يحتوي على تطبيق SVG مثبَّت.
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) تم إنشاؤها باستخدام ترميز SVG، تتمثل إحدى طرق التعامل مع هذا الأمر في حفظ الملف، وتشغيل تطبيق تعديل 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:

هدف المشاركة على الويب (الملفات)

وفي المقابل، يمكن أن يعمل SVGcode أيضًا كهدف للمشاركة ويتلقّى الملفات من تطبيقات أخرى. لتنفيذ هذا الإجراء، يحتاج التطبيق إلى إعلام نظام التشغيل عبر Web Share 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:

الخلاصة

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

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

شكر وتقدير

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