API truy cập phông chữ cục bộ cung cấp cơ chế truy cập dữ liệu phông chữ được cài đặt cục bộ của người dùng, bao gồm các chi tiết cấp cao hơn như tên, kiểu và họ, cũng như byte thô của tệp phông chữ cơ bản. Tìm hiểu cách ứng dụng chỉnh sửa SVG Boxy SVG sử dụng API này.
Giới thiệu
(Bài viết này cũng có sẵn ở dạng video.)
Boxy SVG là trình chỉnh sửa đồ hoạ vectơ. Trường hợp sử dụng chính là chỉnh sửa các bản vẽ ở định dạng tệp SVG để tạo hình minh hoạ, biểu trưng, biểu tượng và các thành phần khác của thiết kế đồ hoạ. Trò chơi này do nhà phát triển Ba Lan Jaros Camera Foksa phát triển và phát hành lần đầu vào ngày 15 tháng 3 năm 2013. Jaroslevaw điều hành một blog Boxy SVG để thông báo về các tính năng mới mà anh thêm vào ứng dụng. Nhà phát triển này rất ủng hộ Project Fugu của Chromium và thậm chí còn gắn thẻ Fugu trên công cụ theo dõi ý tưởng của ứng dụng.
API truy cập phông chữ cục bộ trong Boxy SVG
Một tính năng bổ sung mà Jarosowski đã đăng trên blog là Local Font Access API (API Truy cập phông chữ cục bộ). API truy cập phông chữ cục bộ cho phép người dùng truy cập phông chữ được cài đặt trên máy, bao gồm cả các chi tiết cấp cao hơn như tên, kiểu và họ, cũng như các byte thô của tệp phông chữ cơ bản. Trong ảnh chụp màn hình sau đây, bạn có thể thấy cách tôi cấp cho ứng dụng quyền truy cập vào các phông chữ được cài đặt cục bộ trên MacBook và chọn phông chữ Marker Felt cho văn bản của mình.
Mã cơ bản khá đơn giản. Khi người dùng mở bộ chọn bộ phông chữ lần đầu tiên, trước tiên, ứng dụng sẽ kiểm tra xem trình duyệt web có hỗ trợ API truy cập phông chữ cục bộ hay không.
Tính năng này cũng kiểm tra phiên bản thử nghiệm cũ của API và sử dụng phiên bản đó nếu có. Kể từ năm 2023, bạn có thể yên tâm bỏ qua API cũ vì API này chỉ hoạt động trong một thời gian ngắn thông qua các cờ Chrome thử nghiệm. Tuy nhiên, một số công cụ phái sinh của Chromium vẫn có thể sử dụng API này.
let isLocalFontsApiEnabled = (
// Local Font Access API, Chrome >= 102
window.queryLocalFonts !== undefined ||
// Experimental Local Font Access API, Chrome < 102
navigator.fonts?.query !== undefined
);
Nếu API truy cập phông chữ cục bộ không có sẵn, bộ chọn bộ phông chữ sẽ chuyển sang màu xám. Người dùng sẽ thấy một văn bản giữ chỗ thay cho danh sách phông chữ:
if (isLocalFontsApiEnabled === false) {
showPlaceholder("no-local-fonts-api");
return;
}
Nếu không, API truy cập phông chữ cục bộ sẽ được dùng để truy xuất danh sách tất cả phông chữ từ hệ điều hành. Hãy lưu ý khối try…catch
cần thiết để xử lý các lỗi về quyền đúng cách.
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);
}
}
Sau khi truy xuất danh sách phông chữ trên máy, hệ thống sẽ tạo một fontsIndex
đơn giản và đã chuẩn hoá từ đó:
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();
}
Sau đó, chỉ mục phông chữ đã chuẩn hoá được lưu trữ trong cơ sở dữ liệu IndexedDB để có thể dễ dàng truy vấn, chia sẻ giữa các thực thể ứng dụng và lưu giữ giữa các phiên. Boxy SVG sử dụng Dexie.js để quản lý cơ sở dữ liệu:
let database = new Dexie("LocalFontsManager");
database.version(1).stores({cache: "family"}).
await database.cache.clear();
await database.cache.bulkPut(fontsIndex);
Sau khi cơ sở dữ liệu được điền sẵn, tiện ích bộ chọn phông chữ có thể truy vấn cơ sở dữ liệu đó và cho thấy kết quả trên màn hình:
Điều đáng nói đến là Boxy SVG hiển thị danh sách trong một phần tử tuỳ chỉnh có tên là <bx-fontfamilypicker>
và tạo kiểu cho từng mục trong danh sách phông chữ để nó xuất hiện trong bộ phông chữ cụ thể. Để tách biệt với phần còn lại của trang, Boxy SVG sử dụng Shadow DOM trong phần tử này và các phần tử tuỳ chỉnh khác.
Kết luận
Tính năng phông chữ trên máy thực sự phổ biến, khi người dùng thích sử dụng phông chữ trên máy để thiết kế và sáng tạo. Khi hình dạng API thay đổi và tính năng bị lỗi trong thời gian ngắn, người dùng sẽ ghi chú ngay lập tức. Jarosowski đã nhanh chóng thay đổi mã này thành mô hình phòng thủ mà bạn có thể thấy trong đoạn mã ở trên. Mô hình này hoạt động với Chrome phiên bản mới nhất cũng như các cấu trúc phái sinh khác của Chromium có thể chưa được chuyển sang phiên bản mới nhất. Hãy thử sử dụng SVG Boxy và nhớ kiểm tra các phông chữ được cài đặt cục bộ của bạn. Bạn có thể khám phá một số tác phẩm kinh điển đã bị lãng quên từ lâu như Zapf Dingbats hoặc Webdings.