כאן מוסבר איך ממשק ה-API של Local Font Access מאפשר לכם לגשת לגופנים שהמשתמש התקין באופן מקומי ולקבל פרטים ברמה נמוכה לגביהם
פורסם: 24 באוגוסט 2020
גופנים בטוחים לשימוש באינטרנט
אם אתם מפתחי אתרים ותיקים, יכול להיות שאתם זוכרים את מה שנקרא גופנים בטוחים לשימוש באינטרנט.
הגופנים האלה זמינים כמעט בכל המקרים של מערכות ההפעלה הנפוצות ביותר (כלומר 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.
תוקף יכול לנסות לגלות באיזו חברה מישהו עובד על ידי בדיקה של קיומם של מספר גדול של גופנים ארגוניים ידועים, כמו Google Sans. התוקף ינסה לרנדר טקסט שמוגדר בפונטים האלה על בד ציור ולמדוד את הגליפים. אם הגליפים תואמים לצורה הידועה של הגופן הארגוני, התוקף השיג התאמה. אם הגליפים לא תואמים, התוקף יודע שהמערכת השתמשה בגופן חלופי שמוגדר כברירת מחדל, כי הגופן של החברה לא הותקן. פרטים מלאים על המתקפה הזו ועל מתקפות אחרות של טביעת אצבע בדפדפן זמינים במאמר הסקירה של Laperdix ועמיתיו.
גם אם לא משתמשים בגופנים של החברה, אפילו רשימת הגופנים המותקנים יכולה לשמש לזיהוי. המצב עם וקטור התקפה הזה החמיר כל כך, שבזמן האחרון צוות WebKit החליט "לכלול [ברשימת הגופנים הזמינים] רק גופני אינטרנט וגופנים שמגיעים עם מערכת ההפעלה, אבל לא גופנים שהמשתמש התקין באופן מקומי". (ואני כאן, עם מאמר על מתן גישה לגופנים מקומיים).
Local Font Access API
יכול להיות שההתחלה של המאמר הזה גרמה לכם למצב רוח רע. באמת אי אפשר לקבל דברים נחמדים? אל דאגה. אנחנו חושבים שאפשר, ואולי הכול עוד לא אבוד. אבל קודם, אענה על שאלה שאולי עולה לך בראש.
למה אנחנו צריכים את Local Font Access API אם יש גופני אינטרנט?
בעבר היה קשה לספק באינטרנט כלים מקצועיים לעיצוב ולגרפיקה. אחת הבעיות היא חוסר היכולת לגשת למגוון המלא של גופנים מקצועיים עם רמזים שמעצבים התקינו באופן מקומי, ולהשתמש בהם. גופני אינטרנט מאפשרים כמה תרחישי שימוש בפרסום, אבל לא מאפשרים גישה פרוגרמטית לצורות של גליפים וקטוריים ולטבלאות גופנים שמשמשות רסטריזטורים לעיבוד של קווי המתאר של הגליפים. באופן דומה, אין דרך לגשת לנתונים הבינאריים של גופן אינטרנט.
- כדי ליישם פריסה משלהם של OpenType ולאפשר לכלים לעיצוב להתחבר ברמות נמוכות יותר, לביצוע פעולות כמו הפעלת מסנני וקטור או טרנספורמציות על צורות הגליפים, כלים לעיצוב צריכים גישה לבייטים של הגופן.
- יכול להיות שלמפתחים יש ערימות של גופנים מדור קודם לאפליקציות שלהם, שהם מעבירים לאינטרנט. כדי להשתמש בחבילות האלה, בדרך כלל נדרשת גישה ישירה לנתוני הגופן, דבר שגופני אינטרנט לא מספקים.
- יכול להיות שחלק מהגופנים לא מורשים להפצה באינטרנט. לדוגמה, ל-Linotype יש רישיון לכמה גופנים שכולל רק שימוש במחשב.
ה-API של גישה לגופנים מקומיים נועד לפתור את האתגרים האלה. הוא מורכב משני חלקים:
- font enumeration API, שמאפשר למשתמשים להעניק גישה לכל קבוצת הגופנים הזמינה במערכת.
- מכל תוצאת ספירה, אפשר לבקש גישה למאגר SFNT ברמה נמוכה (מכוונת בייטים) שכוללת את נתוני הגופן המלאים.
תמיכה בדפדפנים
איך משתמשים ב-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 כזה של המערכת עלולה לחשוף נתונים נוספים שאפשר להשתמש בהם לזיהוי מכשיר (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 ולספר לנו איפה ואיך אתם משתמשים בו.
קישורים שימושיים
- הסבר
- Spec draft
- באג ב-Chromium בנוגע לספירת גופנים
- באג ב-Chromium שקשור לגישה לטבלת גופנים
- כניסה ל-ChromeStatus
- מאגר GitHub
- בדיקת תגים
- העמדה של Mozilla בנוגע לתקנים
תודות
מפרט Local Font Access API נערך על ידי Emil A. Eklund, Alex Russell, Joshua Bell ו-Olivier Yiptong. המאמר הזה נבדק על ידי ג'ו מדלי, דומיניק רטשס ואוליבייה ייפטונג.