از تایپوگرافی پیشرفته با فونت های محلی استفاده کنید

بیاموزید که چگونه API دسترسی به فونت محلی به شما امکان می‌دهد به فونت‌های نصب شده محلی کاربر دسترسی پیدا کنید و جزئیات سطح پایین در مورد آنها را بدست آورید.

منتشر شده: ۲۴ آگوست ۲۰۲۰

فونت‌های ایمن وب

اگر به اندازه کافی در توسعه وب فعالیت داشته باشید، ممکن است فونت‌های به اصطلاح ایمن وب را به خاطر داشته باشید. این فونت‌ها تقریباً در تمام نمونه‌های سیستم عامل‌های پرکاربرد (یعنی ویندوز، macOS، رایج‌ترین توزیع‌های لینوکس، اندروید و iOS) در دسترس هستند. در اوایل دهه 2000، مایکروسافت حتی ابتکاری به نام فونت‌های هسته TrueType برای وب را آغاز کرد که این فونت‌ها را برای دانلود رایگان ارائه می‌داد با این هدف که "هر زمان که از وب‌سایتی که آنها را مشخص می‌کند بازدید می‌کنید، صفحات را دقیقاً همانطور که طراح سایت در نظر گرفته است، خواهید دید" . بله، این شامل سایت‌هایی می‌شد که در Comic Sans MS تنظیم شده‌اند. در اینجا یک مجموعه فونت ایمن وب کلاسیک (با جایگزینی نهایی هر فونت sans-serif ) وجود دارد که ممکن است به این شکل باشد:

body {
  font-family: Helvetica, Arial, sans-serif;
}

فونت‌های وب

روزهایی که فونت‌های ایمن برای وب واقعاً اهمیت داشتند، مدت‌هاست که گذشته است. امروزه، ما فونت‌های وب داریم که برخی از آنها حتی فونت‌های متغیری هستند که می‌توانیم با تغییر مقادیر محورهای مختلف، آنها را بیشتر تغییر دهیم. می‌توانید با تعریف بلوک @font-face در ابتدای CSS، که فایل(های) فونت مورد نظر برای دانلود را مشخص می‌کند، از فونت‌های وب استفاده کنید:

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: url('flamboyant.woff2');
}

پس از این، می‌توانید طبق معمول با مشخص کردن font-family از فونت وب سفارشی استفاده کنید:

body {
  font-family: 'FlamboyantSansSerif';
}

فونت‌های محلی به عنوان بردار اثر انگشت

بیشتر فونت‌های وب از وب می‌آیند. با این حال، یک واقعیت جالب این است که ویژگی src در تعریف @font-face ، جدا از تابع url() ، یک تابع local() را نیز می‌پذیرد. این امر امکان بارگذاری فونت‌های سفارشی را به صورت محلی فراهم می‌کند (تعجب‌آور است!). اگر کاربر FlamboyantSansSerif را روی سیستم عامل خود نصب کرده باشد، به جای دانلود، از نسخه محلی استفاده می‌شود:

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: local('FlamboyantSansSerif'), url('flamboyant.woff2');
}

این رویکرد یک مکانیزم پشتیبان خوب ارائه می‌دهد که به طور بالقوه پهنای باند را ذخیره می‌کند. متأسفانه در اینترنت نمی‌توانیم چیزهای خوبی داشته باشیم. مشکل تابع local() این است که می‌تواند برای اثر انگشت مرورگر مورد سوءاستفاده قرار گیرد. مشخص شده است که لیست فونت‌هایی که یک کاربر نصب کرده است می‌تواند بسیار قابل شناسایی باشد. بسیاری از شرکت‌ها فونت‌های شرکتی خود را دارند که روی لپ‌تاپ‌های کارمندان نصب می‌شوند. به عنوان مثال، گوگل یک فونت شرکتی به نام Google Sans دارد.

برنامه‌ی Font Book مک‌او‌اس پیش‌نمایشی از فونت Google Sans را نشان می‌دهد.
فونت گوگل سنس که روی لپ‌تاپ یکی از کارمندان گوگل نصب شده است.

یک مهاجم می‌تواند با آزمایش وجود تعداد زیادی فونت شرکتی شناخته‌شده مانند Google Sans ، سعی کند تشخیص دهد که فرد برای کدام شرکت کار می‌کند. مهاجم سعی می‌کند متن تنظیم‌شده با این فونت‌ها را روی یک بوم رندر کند و گلیف‌ها را اندازه‌گیری کند. اگر گلیف‌ها با شکل شناخته‌شده فونت شرکتی مطابقت داشته باشند، مهاجم موفق شده است. اگر گلیف‌ها مطابقت نداشته باشند، مهاجم می‌داند که از یک فونت جایگزین پیش‌فرض استفاده شده است زیرا فونت شرکتی نصب نشده است. برای جزئیات کامل در مورد این و سایر حملات اثر انگشت مرورگر، مقاله نظرسنجی Laperdix و همکاران را مطالعه کنید.

جدا از فونت‌های شرکتی، حتی فهرست فونت‌های نصب‌شده هم می‌تواند هویت فرد را مشخص کند. وضعیت این بردار حمله آنقدر بد شده است که اخیراً تیم WebKit تصمیم گرفته است که «فقط فونت‌های وب و فونت‌هایی را که با سیستم عامل ارائه می‌شوند [در فهرست فونت‌های موجود] لحاظ کند، اما فونت‌های نصب‌شده توسط کاربر را نه» . (و من اینجا هستم، با مقاله‌ای در مورد اعطای دسترسی به فونت‌های محلی.)

API دسترسی محلی به فونت

ممکن است ابتدای این مقاله شما را در حالت منفی قرار داده باشد. آیا واقعاً می‌توانیم چیزهای خوب نداشته باشیم؟ نگران نباشید. ما فکر می‌کنیم می‌توانیم، و شاید همه چیز ناامیدکننده نباشد . اما اول، اجازه دهید به سوالی که ممکن است از خودتان بپرسید پاسخ دهم.

چرا وقتی فونت‌های وب وجود دارند، به API دسترسی به فونت محلی نیاز داریم؟

ارائه ابزارهای طراحی و گرافیکی با کیفیت حرفه‌ای از دیرباز در وب دشوار بوده است. یکی از موانع، عدم توانایی در دسترسی و استفاده از طیف کامل فونت‌های حرفه‌ای ساخته شده و دارای راهنما بوده است که طراحان به صورت محلی نصب کرده‌اند. فونت‌های وب برخی از موارد استفاده انتشار را ممکن می‌سازند، اما نمی‌توانند دسترسی برنامه‌نویسی شده به اشکال برداری گلیف و جداول فونت مورد استفاده توسط رسترایزرها برای رندر کردن طرح‌های گلیف را فراهم کنند. به همین ترتیب، هیچ راهی برای دسترسی به داده‌های دودویی یک فونت وب وجود ندارد.

  • ابزارهای طراحی برای انجام پیاده‌سازی طرح‌بندی OpenType خود و اجازه دادن به ابزارهای طراحی برای اتصال در سطوح پایین‌تر، برای اقداماتی مانند انجام فیلترهای برداری یا تبدیل‌ها روی اشکال گلیف، به بایت‌های فونت نیاز دارند.
  • توسعه‌دهندگان ممکن است برای برنامه‌های خود که به وب می‌آورند، مجموعه فونت‌های قدیمی داشته باشند. برای استفاده از این مجموعه‌ها، معمولاً نیاز به دسترسی مستقیم به داده‌های فونت دارند، چیزی که فونت‌های وب ارائه نمی‌دهند.
  • ممکن است برخی از فونت‌ها برای ارائه در وب مجوز نداشته باشند. برای مثال، Linotype برای برخی از فونت‌ها مجوزی دارد که فقط شامل استفاده در دسکتاپ می‌شود.

API دسترسی محلی به فونت تلاشی برای حل این چالش‌ها است و از دو بخش تشکیل شده است:

  • یک API برای شمارش فونت‌ها که به کاربران اجازه می‌دهد به مجموعه کامل فونت‌های سیستمی موجود دسترسی داشته باشند.
  • از هر نتیجه شمارش، امکان درخواست دسترسی سطح پایین (بایت‌گرا) به کانتینر SFNT که شامل داده‌های کامل فونت است، وجود دارد.

پشتیبانی مرورگر

Browser Support

  • کروم: ۱۰۳.
  • لبه: ۱۰۳.
  • فایرفاکس: پشتیبانی نمی‌شود.
  • سافاری: پشتیبانی نمی‌شود.

Source

نحوه استفاده از API دسترسی به فونت محلی

تشخیص ویژگی

برای بررسی اینکه آیا API دسترسی به فونت محلی پشتیبانی می‌شود، از دستور زیر استفاده کنید:

if ('queryLocalFonts' in window) {
  // The Local Font Access API is supported
}

شمارش فونت‌های محلی

برای دریافت لیستی از فونت‌های نصب‌شده‌ی محلی، باید تابع window.queryLocalFonts() را فراخوانی کنید. اولین بار، این تابع یک درخواست مجوز ایجاد می‌کند که کاربر می‌تواند آن را تأیید یا رد کند. اگر کاربر درخواست فونت‌های محلی خود را تأیید کند، مرورگر آرایه‌ای از داده‌های فونت‌ها را برمی‌گرداند که می‌توانید روی آن حلقه بزنید. هر فونت به عنوان یک شیء FontData با ویژگی‌های family (برای مثال، "Comic Sans MS"fullName (برای مثال، "Comic Sans MS"postscriptName (برای مثال، "ComicSansMS" ) و style (برای مثال، "Regular" ) نمایش داده می‌شود.

// Query for all available fonts and log metadata.
try {
  const availableFonts = await window.queryLocalFonts();
  for (const fontData of availableFonts) {
    console.log(fontData.postscriptName);
    console.log(fontData.fullName);
    console.log(fontData.family);
    console.log(fontData.style);
  }
} catch (err) {
  console.error(err.name, err.message);
}

اگر فقط به زیرمجموعه‌ای از فونت‌ها علاقه‌مند هستید، می‌توانید با اضافه کردن پارامتر postscriptNames ، آنها را بر اساس نام‌های PostScript نیز فیلتر کنید.

const availableFonts = await window.queryLocalFonts({
  postscriptNames: ['Verdana', 'Verdana-Bold', 'Verdana-Italic'],
});

دسترسی به داده‌های SFNT

دسترسی کامل به SFNT از طریق متد blob() از شیء FontData امکان‌پذیر است. SFNT یک فرمت فایل فونت است که می‌تواند شامل فونت‌های دیگری مانند PostScript، TrueType، OpenType، Web Open Font Format (WOFF) و موارد دیگر باشد.

try {
  const availableFonts = await window.queryLocalFonts({
    postscriptNames: ['ComicSansMS'],
  });
  for (const fontData of availableFonts) {
    // `blob()` returns a Blob containing valid and complete
    // SFNT-wrapped font data.
    const sfnt = await fontData.blob();
    // Slice out only the bytes we need: the first 4 bytes are the SFNT
    // version info.
    // Spec: https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
    const sfntVersion = await sfnt.slice(0, 4).text();

    let outlineFormat = 'UNKNOWN';
    switch (sfntVersion) {
      case '\x00\x01\x00\x00':
      case 'true':
      case 'typ1':
        outlineFormat = 'truetype';
        break;
      case 'OTTO':
        outlineFormat = 'cff';
        break;
    }
    console.log('Outline format:', outlineFormat);
  }
} catch (err) {
  console.error(err.name, err.message);
}

نسخه آزمایشی

می‌توانید API دسترسی محلی به فونت را در نسخه آزمایشی (دمو) در عمل مشاهده کنید. حتماً کد منبع را نیز بررسی کنید. این نسخه آزمایشی یک عنصر سفارشی به نام <font-select> را نشان می‌دهد که یک انتخابگر فونت محلی را پیاده‌سازی می‌کند.

ملاحظات حریم خصوصی

به نظر می‌رسد مجوز "local-fonts" سطحی بسیار قابل ردیابی را فراهم می‌کند. با این حال، مرورگرها آزاد هستند هر چیزی را که دوست دارند برگردانند. به عنوان مثال، مرورگرهای متمرکز بر ناشناس بودن ممکن است فقط مجموعه‌ای از فونت‌های پیش‌فرض داخلی مرورگر را ارائه دهند. به طور مشابه، مرورگرها ملزم به ارائه داده‌های جدول دقیقاً همانطور که روی دیسک ظاهر می‌شوند، نیستند.

در صورت امکان، API دسترسی محلی به فونت به گونه‌ای طراحی شده است که فقط اطلاعات مورد نیاز برای فعال کردن موارد استفاده ذکر شده را نمایش دهد. APIهای سیستم ممکن است لیستی از فونت‌های نصب شده را نه به صورت تصادفی یا مرتب، بلکه به ترتیب نصب فونت تولید کنند. بازگرداندن دقیقاً لیست فونت‌های نصب شده ارائه شده توسط چنین API سیستمی می‌تواند داده‌های اضافی را که ممکن است برای انگشت‌نگاری استفاده شوند، نمایش دهد و موارد استفاده‌ای که می‌خواهیم فعال کنیم با حفظ این ترتیب کمکی نمی‌کنند. در نتیجه، این API مستلزم آن است که داده‌های برگشتی قبل از بازگشت مرتب شوند.

امنیت و مجوزها

تیم کروم، رابط برنامه‌نویسی کاربردی دسترسی به فونت محلی (Local Font Access API) را با استفاده از اصول اصلی تعریف‌شده در «کنترل دسترسی به ویژگی‌های قدرتمند پلتفرم وب» ، شامل کنترل کاربر، شفافیت و ارگونومی، طراحی و پیاده‌سازی کرده است.

کنترل کاربر

دسترسی به فونت‌های یک کاربر کاملاً تحت کنترل اوست و تا زمانی که مجوز "local-fonts" که در رجیستری مجوزها ذکر شده است، اعطا نشود، مجاز نخواهد بود.

شفافیت

اینکه آیا به سایتی اجازه دسترسی به فونت‌های محلی کاربر داده شده است یا خیر، در برگه اطلاعات سایت قابل مشاهده خواهد بود.

تداوم مجوز

مجوز "local-fonts" بین بارگذاری مجدد صفحه حفظ خواهد شد. این مجوز را می‌توان از طریق برگه اطلاعات سایت لغو کرد.

بازخورد

تیم کروم می‌خواهد از تجربیات شما در مورد رابط برنامه‌نویسی کاربردی دسترسی به فونت محلی (Local Font Access API) مطلع شود.

در مورد طراحی API به ما بگویید

آیا چیزی در مورد API وجود دارد که آنطور که انتظار داشتید کار نمی‌کند؟ یا متدها یا ویژگی‌هایی وجود ندارند که برای پیاده‌سازی ایده خود به آنها نیاز دارید؟ در مورد مدل امنیتی سؤال یا نظری دارید؟ یک مشکل مشخصات را در مخزن مربوطه GitHub ثبت کنید، یا نظرات خود را به یک مشکل موجود اضافه کنید.

گزارش مشکل در پیاده‌سازی

آیا در پیاده‌سازی کروم اشکالی پیدا کردید؟ یا پیاده‌سازی با مشخصات متفاوت است؟ یک اشکال را در new.crbug.com ثبت کنید. حتماً تا حد امکان جزئیات، دستورالعمل‌های ساده برای بازتولید را ذکر کنید و Blink>Storage>FontAccess در کادر Components وارد کنید.

نمایش پشتیبانی از API

آیا قصد دارید از API دسترسی فونت محلی استفاده کنید؟ پشتیبانی عمومی شما به تیم کروم کمک می‌کند تا ویژگی‌ها را اولویت‌بندی کند و به سایر فروشندگان مرورگر نشان می‌دهد که پشتیبانی از آنها چقدر حیاتی است.

با استفاده از هشتگ #LocalFontAccess یک توییت به @ChromiumDev ارسال کنید و به ما بگویید که کجا و چگونه از آن استفاده می‌کنید.

تقدیرنامه‌ها

مشخصات رابط برنامه‌نویسی کاربردی دسترسی به فونت محلی (Local Font Access API) توسط امیل ای. اکلوند ، الکس راسل ، جاشوا بل و اولیویه ییپتونگ ویرایش شده است. این مقاله توسط جو مدلی ، دومینیک روتشس و اولیویه ییپتونگ بررسی شده است.