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

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

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

Local Font Access API

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

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

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

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

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

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

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

תמיכה בדפדפן

  • Chrome: 103.
  • קצה: 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 באמצעות עקרונות הליבה שהוגדרו ב-Controlling Access to Powerful Web Platform Features, כולל בקרת משתמשים, שקיפות וארגונומיה.

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

הגישה לגופנים של משתמש נמצאת בשליטתו המלאה, והיא לא תתאפשר אלא אם תוענק ההרשאה "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. הבדיקה של המאמר בוצעה על ידי Joe Medley,‏ Dominik Röttsches ו-Olivier Yiptong. התמונה הראשית (Hero) של Brett Jordan ב-Unsplash.