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

איך ממשק ה-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 ברמה נמוכה (ממוקדת-בייט) שכוללת את נתוני הגופן המלאים.

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

Browser Support

  • Chrome: 103.
  • Edge: 103.
  • Firefox: not supported.
  • Safari: not supported.

Source

איך משתמשים ב-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.

תמיכה ב-API

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

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

תודות

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