איך אפליקציית עריכת התמונות וקטורית Boxy SVG משתמשת ב-Local Font Access API כדי לאפשר למשתמשים לבחור את הגופנים המקומיים המועדפים עליהם

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

תומאס סטיינר
תומאס סטיינר
יארק פוקסה
יארק פוקסה

מבוא

(המאמר הזה זמין גם בצורת סרטון.)

Boxy SVG היא כלי לעריכת גרפיקה וקטורית. התרחיש העיקרי לדוגמה הוא עריכת שרטוטים בפורמט הקובץ SVG ליצירת איורים, סמלי לוגו, סמלים ואלמנטים אחרים של עיצוב גרפי. הכלי פותח על ידי המפתח הפולני ירוסלב פוקסה, והוא הושק לראשונה ב-15 במרץ 2013. ירוסלאו מפעיל בלוג Boxy SVG שבו הוא מכריז על תכונות חדשות שהוא מוסיף לאפליקציה. המפתח הוא תומך נלהב ב-Project Fugu של Chromium, ויש לו תג Fugu במעקב הרעיונות של האפליקציה.

אפליקציית Boxy SVG שעורכים את הסמל של Project Fugu בפורמט SVG.

Local Font Access API ב-Boxy SVG

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

אפליקציית Boxy SVG עורךת את הסמל של Project Fugu SVG ומוסיפה את הטקסט 'Project Fugu Rocks' בסמן הגופן של Felt, שמוצג בבוחר הגופנים.

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

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

let isLocalFontsApiEnabled = (
  // Local Font Access API, Chrome >= 102
  window.queryLocalFonts !== undefined ||
  // Experimental Local Font Access API, Chrome < 102
  navigator.fonts?.query !== undefined
);

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

if (isLocalFontsApiEnabled === false) {
  showPlaceholder("no-local-fonts-api");
  return;
}

בוחר גופנים שבו מוצגת ההודעה &#39;הדפדפן שלך לא תומך ב-Local Font Access API&#39;

במקרים אחרים, ה-Local Font Access API משמש לאחזור הרשימה של כל הגופנים ממערכת ההפעלה. שימו לב לבלוק try…catch, שנדרש כדי לטפל בשגיאות הרשאה כראוי.

let localFonts;

if (isLocalFontsApiEnabled === true) {
  try {
    // Local Font Access API, Chrome >= 102
    if (window.queryLocalFonts) {
      localFonts = await window.queryLocalFonts();
    }
    // Experimental Local Font Access API, Chrome < 102
    else if (navigator.fonts?.query) {
      localFonts = await navigator.fonts.query({
        persistentAccess: true,
      });
    }
  } catch (error) {
    showError(error.message, error.name);
  }
}

אחרי אחזור רשימת הגופנים המקומיים, נוצר ממנה fontsIndex פשוט ומנורמל:

let fontsIndex = [];

for (let localFont of localFonts) {
  let face = "400";

  // Determine the face name
  {
    let subfamily = localFont.style.toLowerCase();
    subfamily = subfamily.replaceAll(" ", "");
    subfamily = subfamily.replaceAll("-", "");
    subfamily = subfamily.replaceAll("_", "");

    if (subfamily.includes("thin")) {
      face = "100";
    } else if (subfamily.includes("extralight")) {
      face = "200";
    } else if (subfamily.includes("light")) {
      face = "300";
    } else if (subfamily.includes("medium")) {
      face = "500";
    } else if (subfamily.includes("semibold")) {
      face = "600";
    } else if (subfamily.includes("extrabold")) {
      face = "800";
    } else if (subfamily.includes("ultrabold")) {
      face = "900";
    } else if (subfamily.includes("bold")) {
      face = "700";
    }

    if (subfamily.includes("italic")) {
      face += "i";
    }
  }

  let descriptor = fontsIndex.find((descriptor) => {
    return descriptor.family === localFont.family);
  });

  if (descriptor) {
    if (descriptor.faces.includes(face) === false) {
      descriptor.faces.push(face);
    }
  } else {
    let descriptor = {
      family: localFont.family,
      faces: [face],
    };

    fontsIndex.push(descriptor);
  }
}

for (let descriptor of fontsIndex) {
  descriptor.faces.sort();
}

לאחר מכן, אינדקס הגופנים המנורמל נשמר במסד הנתונים IndexedDB כדי שניתן יהיה לשלוח אליו שאילתות, לשתף אותו בין מופעים שונים של אפליקציות ולשמור אותו בקלות בין סשנים. בפורמט Boxy SVG נעשה שימוש ב-Dexie.js כדי לנהל את מסד הנתונים:

let database = new Dexie("LocalFontsManager");
database.version(1).stores({cache: "family"}).
await database.cache.clear();
await database.cache.bulkPut(fontsIndex);

הקטע &#39;אחסון כלי הפיתוח ל-Chrome&#39; מציג את הטבלה IndexedDB עם מטמון הגופנים.

לאחר אכלוס מסד הנתונים, הווידג'ט של בוחר הגופנים יכול להריץ שאילתות עליו ולהציג את התוצאות על המסך:

בוחר הגופנים מאוכלס בגופנים.

חשוב לציין שפורמט Boxy SVG מעבד את הרשימה באלמנט מותאם אישית בשם <bx-fontfamilypicker> ומעצב כל פריט ברשימת הגופנים כך שהוא יוצג במשפחת הגופנים המסוימת. כדי לבודד משאר הדף, Boxy SVG משתמש ב-Shadow DOM ברכיב הזה וברכיבים מותאמים אישית אחרים.

חלונית &#39;רכיבי כלי הפיתוח&#39; ב-Chrome שבה רואים את בוחר הגופנים שנבדק: רכיב מותאם אישית בשם &#39;bx-fontfamiliy Picker&#39;.

מסקנות

התכונה של גופנים מקומיים הפכה לפופולרית מאוד, והמשתמשים נהנו מגישה לגופנים המקומיים בזכות העיצובים והיצירות שלהם. אם צורת ה-API השתנתה והתכונה התבטלה לזמן קצר, המשתמשים ציינו זאת מיד. ירוסלב שינתה במהירות את הקוד לתבנית ההגנה שניתן לראות בקטע הקוד שלמעלה, שעובד עם הגרסה העדכנית של Chrome ונגזרות אחרות של Chromium שייתכן שלא עברו לגרסה האחרונה. נסו את ערכת ה-SVG של Boxy ואל תשכחו לבדוק את הגופנים שמותקנים באופן מקומי. יכול להיות שתגלו כמה קלאסיקות נשכחות מזמן, כמו Zapf Dingbats או Webdings.