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
أولاً، أريد أن أعرض لك كيفية استخدام التطبيق. سأبدأ بالصورة التمهيدية لحدث Chrome Dev Summit التي نزّلتها من قناة ChromiumDev على Twitter. هذه صورة نقطية بتنسيق PNG أسحبها بعد ذلك إلى تطبيق SVGcode. عند إسقاط الملف، يتتبّع التطبيق لون الصورة، إلى أن يظهر إصدار مُوجَّه من الإدخال. يمكنني الآن تكبير الصورة، وكما ترى، تبقى الحواف حادة. ولكن عند تكبير شعار Chrome، يمكنك ملاحظة أنّ عملية التتبُّع لم تكن مثالية، لا سيما أنّ الخطوط الخارجية للشعار تبدو مموّهة بعض الشيء. يمكنني تحسين النتيجة من خلال إزالة البقع من عملية التتبُّع عن طريق قمع البقع التي يصل حجمها إلى خمسة بكسل مثلاً.
تأثير الصورة المطبوعة في SVGcode
من الخطوات المهمة لتحويل الصور إلى أشكال هندسية، خاصةً الصور الفوتوغرافية، هي تحويل ملف الصورة إلى صورة ملصق لتقليل عدد الألوان. يتيح لي SVGcode إجراء ذلك لكل قناة لون، والاطّلاع على ملف SVG الناتج أثناء إجراء التغييرات. وعندما أكون راضيًا عن النتيجة، يمكنني حفظ ملف SVG على محرك الأقراص الثابتة واستخدامه في أي مكان أريد.
واجهات برمجة التطبيقات المستخدَمة في SVGcode
بعد أن اطّلعت على إمكانات التطبيق، سأعرض لك بعض واجهات برمجة التطبيقات التي تساعد في إحداث التغييرات السحرية.
تطبيق ويب تقدّمي
SVGcode هو تطبيق ويب تقدّمي قابل للتثبيت، وبالتالي يمكن استخدامه بلا اتصال بالإنترنت. يستند التطبيق إلى نموذج Vanilla JS لـ Vite.js ويستخدم المكوّن الإضافي الرائج Vite plugin PWA الذي ينشئ عامل خدمة يستخدم Workbox.js في الخلفية. Workbox هي مجموعة من المكتبات التي يمكنها تشغيل مشغّل خدمات جاهز للنشر في "تطبيقات الويب التقدّمية". قد لا يعمل هذا النمط بالضرورة مع جميع التطبيقات، ولكنّه رائع لحالة استخدام SVGcode.
تراكب عناصر التحكّم في النوافذ
لزيادة المساحة المتوفّرة على الشاشة إلى أقصى حد، يستخدم SVGcode تخصيص 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 على لوحة المفاتيح.
اكتسبت واجهة Async Clipboard API مؤخرًا إمكانية التعامل مع صور SVG أيضًا، لذا يمكنك أيضًا نسخ صورة SVG ولصقها في تطبيق آخر لإجراء المزيد من المعالجة.
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 فيملف بيان تطبيق الويب وقائمة انتظار التشغيل، ما يسمح للتطبيق باستخدام الملف الذي تم تمريره.
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);
}
}
}
});
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. نأمل أن يصبح هذا التطبيق أداة أساسية لتلبية احتياجاتك المتعلّقة بمعالجة الصور إلى جانب تطبيقات رائعة أخرى مثل Squoosh أو SVGOMG.
يتوفّر SVGcode على svgco.de. هل لاحظت ما فعلته؟ يمكنك مراجعة رمزها المصدر على GitHub. يُرجى العِلم أنّه بما أنّ Potrace مرخّص بموجب GPL، فإنّ SVGcode مرخّص أيضًا بموجب GPL. مع أطيب التحيّات، نأمل أن يكون SVGcode مفيدًا، ويُلهمك بعض ميزاته في إنشاء تطبيقك التالي.
الشكر والتقدير
راجع جو ميديل هذه المقالة.