วิธีที่แอปแก้ไขรูปภาพเวกเตอร์ Boxy SVG ใช้ Local Font Access API เพื่อให้ผู้ใช้เลือกแบบอักษรโปรดในพื้นที่

Local Font Access API มีกลไกในการเข้าถึงข้อมูลแบบอักษรที่ติดตั้งในเครื่องของผู้ใช้ ซึ่งรวมถึงรายละเอียดในระดับที่สูงขึ้น เช่น ชื่อ รูปแบบ และครอบครัว ตลอดจนไบต์ดิบของไฟล์แบบอักษรที่เกี่ยวข้อง ดูว่าแอปแก้ไข SVG ที่ Boxy SVG ใช้ประโยชน์จาก API นี้อย่างไร

เกริ่นนำ

(บทความนี้ยังมีรูปแบบวิดีโออีกด้วย)

Boxy SVG คือเครื่องมือแก้ไขกราฟิกเวกเตอร์ กรณีการใช้งานหลักคือการแก้ไขภาพวาดในรูปแบบไฟล์ SVG เพื่อสร้างภาพประกอบ โลโก้ ไอคอน และองค์ประกอบอื่นๆ ของการออกแบบกราฟิก พัฒนาโดยนักพัฒนาซอฟต์แวร์ชาวโปแลนด์ Jarosław Foksa และเปิดตัวครั้งแรกในวันที่ 15 มีนาคม 2013 Jarosław ทำบล็อก Boxy SVG เพื่อประกาศฟีเจอร์ใหม่ๆ ที่เขาได้เพิ่มลงในแอป นักพัฒนาซอฟต์แวร์สนับสนุน Project Fugu ของ Chromium อย่างเหนียวแน่น และยังมีแท็ก Fugu อยู่ในตัวติดตามแนวคิดของแอปอีกด้วย

แอป Boxy SVG แก้ไขไอคอน SVG ของ Project Fugu

API การเข้าถึงแบบอักษรในเครื่องใน Boxy SVG

Jarosław เพิ่มคุณลักษณะอย่างหนึ่ง เขียนเกี่ยวกับ คือ Local Font Access API Local Font Access API ช่วยให้ผู้ใช้เข้าถึงแบบอักษรที่ติดตั้งอยู่ในเครื่องได้ รวมถึงรายละเอียดในระดับที่สูงขึ้น เช่น ชื่อ รูปแบบ และครอบครัว ตลอดจนไบต์ดิบของไฟล์แบบอักษรที่สำคัญ ในภาพหน้าจอต่อไปนี้ คุณจะเห็นว่าเราได้ให้สิทธิ์แอปในการเข้าถึงแบบอักษรที่ติดตั้งในเครื่อง MacBook และเลือกแบบอักษรมาร์กเกอร์ Felt สำหรับข้อความของฉัน

แอป Boxy SVG ที่แก้ไขไอคอน Project Fugu ของ SVG ได้เพิ่มข้อความ "Project Fugu rocks" ของชุดแบบอักษรในมาร์กเกอร์ Felt ซึ่งแสดงที่เลือกไว้ในเครื่องมือเลือกแบบอักษร

โค้ดที่สำคัญค่อนข้างตรงไปตรงมา เมื่อผู้ใช้เปิดเครื่องมือเลือกชุดแบบอักษรเป็นครั้งแรก แอปพลิเคชันจะตรวจสอบก่อนว่าเว็บเบราว์เซอร์รองรับ Local Font Access API หรือไม่

นอกจากนี้ยังตรวจหา API เวอร์ชันทดลองเวอร์ชันเก่าและใช้หากมี ในปี 2023 คุณข้าม API เก่าได้อย่างปลอดภัยเนื่องจาก API ดังกล่าวมีให้ใช้งานได้เป็นระยะเวลาสั้นๆ ผ่าน Chrome Flag แบบทดลองเท่านั้น แต่อนุพันธ์ของ Chromium บางรายการอาจยังใช้ API นี้อยู่

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;
}

เครื่องมือเลือกแบบอักษรแสดงข้อความ &quot;เบราว์เซอร์ของคุณไม่รองรับ Local Font Access API&quot;

ไม่เช่นนั้นระบบจะใช้ 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);

ส่วนพื้นที่เก็บข้อมูลเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ที่แสดงตาราง IndexedDB พร้อมแคชแบบอักษร

เมื่อฐานข้อมูลได้รับการสร้างแล้ว วิดเจ็ตเครื่องมือเลือกแบบอักษรจะสามารถสืบค้นข้อมูลและแสดงผลลัพธ์บนหน้าจอได้ โดยทำดังนี้

เครื่องมือเลือกแบบอักษรเต็มไปด้วยแบบอักษร

โปรดทราบว่า Boxy SVG จะแสดงผลรายการในองค์ประกอบที่กำหนดเองชื่อ <bx-fontfamilypicker> และกำหนดรูปแบบรายการแบบอักษรแต่ละรายการเพื่อให้แสดงในตระกูลแบบอักษรดังกล่าว Boxy SVG จะใช้ Shadow DOM ในองค์ประกอบนี้และองค์ประกอบที่กำหนดเองอื่นๆ เพื่อแยกส่วนที่เหลือของหน้า

แผงองค์ประกอบ Chrome DevTools ซึ่งแสดงเครื่องมือเลือกแบบอักษรที่กำลังตรวจสอบอยู่: องค์ประกอบที่กำหนดเองชื่อ &quot;bx-fontfamiliypicker&quot;

บทสรุป

ฟีเจอร์แบบอักษรในเครื่องได้รับความนิยมมาก และผู้ใช้ก็เพลิดเพลินกับการใช้งานแบบอักษรในเครื่องสำหรับการออกแบบและผลงานของตนเองได้ เมื่อเปลี่ยนรูปร่าง API และฟีเจอร์เสียหายเป็นระยะเวลาสั้นๆ ผู้ใช้ก็สังเกตเห็นทันที Jarosław สามารถเปลี่ยนแปลงโค้ดเป็นรูปแบบการป้องกันที่คุณเห็นในตัวอย่างด้านบนได้อย่างรวดเร็ว โดยสามารถทำงานกับ Chrome รุ่นล่าสุด และอนุพันธ์ของ Chromium อื่นๆ ที่อาจยังไม่ได้เปลี่ยนเป็นเวอร์ชันล่าสุด ทดลองใช้ Boxy SVG และอย่าลืมดูแบบอักษรที่ติดตั้งในเครื่อง คุณอาจค้นพบวิดีโอคลาสสิกที่ลืมไปแล้วบางส่วน เช่น Zapf Dingbats หรือ Webdings