שימוש בטיפוגרפיה מתקדמת עם גופנים מקומיים

איך ממשק ה-API לגישה לגופנים מקומיים מאפשר לכם לגשת לגופנים שהותקנו באופן מקומי במכשיר של המשתמש ולקבל פרטים ברמה נמוכה עליהם

גופנים בטוחים לאינטרנט

אם אתם מפתחי אתרים ותיקים, יכול להיות שאתם זוכרים את הגופנים המתאימים לאינטרנט. הגופנים האלה ידועים שהם זמינים כמעט בכל המכונות של מערכות ההפעלה הנפוצות ביותר (למשל Windows, macOS, הגרסאות הנפוצות ביותר של Linux, Android ו-iOS). בתחילת שנות ה-2000, Microsoft אפילו הובילה יוזמה שנקראת TrueType core fonts for the Web, שבמסגרתה הפונטים האלה היו זמינים להורדה בחינם. המטרה של היוזמה הזו הייתה "כאשר מבקרים באתר אינטרנט שמציין אותם, רואים את הדפים בדיוק כפי שמי שעיצב את האתר התכוון". כן, הרשימה הזו כללה אתרים שהוגדרו ב-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 יש גופן חברה שנקרא Google Sans.

אפליקציית Font Book ב-macOS שמופיע בה תצוגה מקדימה של הגופן Google Sans.
הפונט Google Sans שמותקן במחשב הנייד של עובד Google.

תוקף יכול לנסות לקבוע באיזו חברה משתמש מסוים עובד על ידי בדיקה של נוכחות של מספר גדול של גופנים ארגוניים ידועים, כמו Google Sans. התוקף ינסה לעבד את הטקסט שהוגדר בגופנים האלה על קנבס ולמדוד את הגליפים. אם הגליפים תואמים לצורה המוכרת של הגופן הארגוני, התוקף מקבל הייט. אם הגופנים לא תואמים, התוקף יודע שנעשה שימוש בגופן חלופי שמוגדר כברירת מחדל כי הגופן העסקי לא הותקן. פרטים מלאים על ההתקפה הזו ועל התקפות אחרות של טביעת אצבע של דפדפן זמינים בסקירה של Laperdix et al.

ניתן לזהות גופנים של חברות אחרות, גם אם ניתן לזהות רק את רשימת הגופנים המותקנים. המצב עם וקטור התקיפה הזה הפך להיות כל כך גרוע, עד שלאחרונה צוות WebKit החליט ש"כולל רק [ברשימת הגופנים הזמינים] של גופנים באינטרנט וגופנים שכלולים במערכת ההפעלה, אבל לא גופנים שהותקנו על ידי המשתמשים". (ואני כאן, עם מאמר בנושא מתן גישה לגופנים מקומיים).

Local Font Access API

ייתכן שבתחילת המאמר הזה קיבלת מצב רוח שלילי. האם באמת אי אפשר ליהנות מדברים נחמדים? אל דאגה. אנחנו חושבים שאנחנו יכולים, ואולי הכול לא אבוד. אבל לפני כן, אני רוצה לענות על שאלה ששאלת את עצמך.

למה אנחנו צריכים את Local Font Access API כשיש גופני אינטרנט?

בעבר היה קשה להציע באינטרנט כלים לעיצוב ולגרפיקה באיכות מקצועית. אחת מהבעיות הייתה חוסר היכולת לגשת למגוון המלא של גופנים שנוצרו באופן מקצועי ועוברים תהליך של הוספת נתונים (hinting) שהמעצבים התקינו באופן מקומי, ולהשתמש בהם. גופני אינטרנט מאפשרים שימוש בתרחישים מסוימים בפרסום, אבל הם לא מאפשרים גישה פרוגרמטית לצורות של גליפים וקטוריים ולטבלאות גופנים שבהן משתמשים לרסטרייזרים לעיבוד קווי הגליף. באותו אופן, אין דרך לגשת לנתונים הבינאריים של גופנים באינטרנט.

  • לכלים לעיצוב צריכה להיות גישה לביטיי הגופן כדי לבצע הטמעה משלהם של פריסה ב-OpenType ולאפשר לכלים לעיצוב להתחבר ברמות נמוכות יותר, לפעולות כמו ביצוע מסננים וקונברטיות של צורות הגליפים.
  • יכול להיות שלמפתחים יש סטאקים קודמים של גופנים לאפליקציות שהם מעבירים לאינטרנט. כדי להשתמש בסטאקים האלה, בדרך כלל נדרשת גישה ישירה לנתוני הגופן, וזוהי תכונה שאין לגופנים באינטרנט.
  • יכול להיות שחלק מהגופנים לא מורשים להעברה באינטרנט. לדוגמה, ל-Linotype יש רישיון לגופנים מסוימים שכולל רק שימוש במחשב.

Local Font Access API הוא ניסיון לפתור את האתגרים האלה. הוא מורכב משני חלקים:

  • API של ספירת גופנים, שמאפשר למשתמשים להעניק גישה לקבוצה המלאה של גופנים זמינים במערכת.
  • מכל תוצאה של ספירה, היכולת לבקש גישה לקונטיינר SFNT ברמה נמוכה (ממוקדת-בייט) שכוללת את נתוני הגופן המלאים.

תמיכה בדפדפנים

תמיכה בדפדפן

  • Chrome: 103.
  • Edge:‏ 103.
  • Firefox: לא נתמך.
  • Safari: לא נתמך.

מקור

איך משתמשים ב-Local Font Access API

זיהוי תכונות

כדי לבדוק אם יש תמיכה ב-Local Font Access 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);
}

אם אתם מעוניינים רק בקבוצת משנה של גופנים, תוכלו לסנן אותם גם לפי השמות שלהם ב-PostScript על ידי הוספת הפרמטר postscriptNames.

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);
}

הדגמה (דמו)

אתם יכולים לראות את Local Font Access API בפעולה בדמו שבהמשך. חשוב לבדוק גם את קוד המקור. בהדגמה מוצג רכיב מותאם אישית בשם <font-select> שמטמיע בוחר גופנים מקומי.

שיקולים בנושא פרטיות

נראה שההרשאה "local-fonts" מספקת משטח שקל מאוד ליצור עליו טביעות אצבע. עם זאת, הדפדפנים יכולים להחזיר כל מה שהם רוצים. לדוגמה, דפדפנים שמתמקדים באנונימיות עשויים לבחור לספק רק קבוצה של גופנים שמוגדרים כברירת מחדל בדפדפן. באופן דומה, הדפדפנים לא נדרשים לספק את נתוני הטבלה בדיוק כפי שהם מופיעים בדיסק.

ככל האפשר, Local Font Access API נועד לחשוף רק את המידע הנדרש כדי לאפשר את התרחישים לדוגמה שצוינו. ממשקי API של המערכת עשויים להפיק רשימה של גופנים מותקנים לא אקראיים או בסדר ממוין, אלא לפי סדר התקנת הגופנים. החזרת הרשימה המדויקת של הגופנים המותקנים שמתקבלת מ-API מערכת כזה עלולה לחשוף נתונים נוספים שעשויים לשמש ליצירת טביעת אצבע, והשמירה על הסדר הזה לא עוזרת לנו להפעיל את תרחישי השימוש שאנחנו רוצים. כתוצאה מכך, ה-API הזה מחייב למיין את הנתונים שמוחזרים לפני שמוחזרים.

אבטחה והרשאות

צוות Chrome תכנן והטמיע את Local Font Access API בהתאם לעקרונות המרכזיים שמוגדרים במאמר בקרת הגישה לתכונות עוצמתיות של פלטפורמת האינטרנט, כולל בקרת משתמשים, שקיפות וארגונומיה.

שליטת משתמשים

הגישה לגופנים של משתמש נמצאת בשליטתו המלאה, והיא לא תתאפשר אלא אם תוענק ההרשאה "local-fonts", כפי שמופיעה במרשם ההרשאות.

שקיפות

תוכלו לראות אם אתר קיבל גישה לגופנים המקומיים של המשתמש בגיליון המידע של האתר.

התמדת ההרשאות

ההרשאה "local-fonts" תישמר בין טעינות מחדש של דפים. אפשר לבטל אותו באמצעות הגיליון פרטי האתר.

משוב

צוות Chrome רוצה לשמוע על החוויה שלכם עם Local Font Access API.

תיאור של עיצוב ה-API

האם יש משהו ב-API שלא פועל כמצופה? או אולי חסרות שיטות או מאפיינים שאתם צריכים כדי להטמיע את הרעיון? יש לכם שאלות או הערות לגבי מודל האבטחה? אפשר לשלוח דיווח על בעיה במפרט במאגר GitHub המתאים, או להוסיף את המחשבות שלכם לבעיה קיימת.

דיווח על בעיה בהטמעה

מצאתם באג בהטמעה של Chrome? או שההטמעה שונה מהמפרט? שולחים דיווח על באג בכתובת new.crbug.com. חשוב לכלול כמה שיותר פרטים, הוראות פשוטות לשחזור הבעיה ולהזין את הערך Blink>Storage>FontAccess בתיבה Components. Glitch הוא כלי מצוין לשיתוף שחזור מהיר וקל של באגים.

תמיכה ב-API

האם אתם מתכננים להשתמש ב-Local Font Access API? התמיכה הציבורית שלכם עוזרת לצוות Chrome לתעדף פיצ'רים ומראה לספקי דפדפנים אחרים עד כמה זה קריטי לתמוך בהם.

אפשר לשלוח ציוץ אל @ChromiumDev עם ההאשטאג #LocalFontAccess ולספר לנו איפה ואיך אתם משתמשים בו.

תודות

Emil A. Eklund,‏ Alex Russell,‏ Joshua Bell ו-Olivier Yiptong. המאמר הזה נבדק על ידי ג'ו מדלי, Dominik Röttsches ו-Olivier Yiptong. התמונה הראשית (Hero) של Brett Jordan ב-Unsplash.