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

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

גופנים שמוצגים בצורה תקינה באינטרנט

אם אתם מפתחי אתרים ותיקים, יכול להיות שאתם זוכרים את מה שנקרא גופנים בטוחים לאינטרנט. הגופנים האלה זמינים כמעט בכל המקרים של מערכות ההפעלה הנפוצות ביותר (כלומר Windows,‏ macOS,‏ הפצות Linux הנפוצות ביותר, Android ו-iOS). בתחילת שנות ה-2000, מיקרוסופט אפילו הובילה יוזמה בשם 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 ועמיתיו.

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

Local Font Access API

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

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

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

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

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

  • font enumeration 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" מספקת פלטפורמה שמאפשרת יצירת טביעת אצבע דיגיטלית ברמה גבוהה. עם זאת, דפדפנים יכולים להחזיר כל מה שהם רוצים. לדוגמה, דפדפנים שמתמקדים באנונימיות עשויים לספק רק קבוצה של גופנים שמוגדרים כברירת מחדל בדפדפן. באופן דומה, דפדפנים לא נדרשים לספק נתוני טבלה בדיוק כמו שהם מופיעים בדיסק.

ה-API של גישה לגופנים מקומיים נועד לחשוף רק את המידע שנדרש כדי להפעיל את תרחישי השימוש שצוינו. מערכות API עשויות ליצור רשימה של גופנים מותקנים שלא מסודרים בסדר אקראי או בסדר מסוים, אלא בסדר ההתקנה של הגופנים. החזרת רשימה מדויקת של הגופנים המותקנים שמוחזרת על ידי API של מערכת כזו עלולה לחשוף נתונים נוספים שאפשר להשתמש בהם לזיהוי מכשיר (fingerprinting). שמירת הסדר הזה לא עוזרת לתרחישי השימוש שאנחנו רוצים לאפשר. לכן, ה-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 ולספר לנו איפה ואיך אתם משתמשים בו.

תודות

מפרט Local Font Access API נערך על ידי Emil A. Eklund, Alex Russell, Joshua Bell ו-Olivier Yiptong. המאמר הזה נבדק על ידי ג'ו מדלי, דומיניק רטשס ואוליבייה ייפטונג. תמונה ראשית (Hero) מאת Brett Jordan ב-Unsplash.