Local Font Access API cung cấp một cơ chế để truy cập vào 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à bộ phông chữ, cũng như các byte thô của các 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 cung cấp dưới dạng video).
Boxy SVG là một trình chỉnh sửa đồ hoạ vectơ. Trường hợp sử dụng chính của ứng dụng này là chỉnh sửa 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 thiết kế đồ hoạ khác. Ứng dụng này do nhà phát triển người Ba Lan Jaroslawaw Foksa phát triển và ban đầu được phát hành vào ngày 15 tháng 3 năm 2013. Jarosław điều hành một blog Boxy SVG, trong đó anh 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 là một người ủng hộ mạnh mẽ Dự án Fugu của Chromium và thậm chí còn có một thẻ Fugu trên trình theo dõi ý tưởng của ứng dụng.
API Quyền truy cập phông chữ cục bộ trong Boxy SVG
Một tính năng bổ sung mà Jarosław đã viết trên blog là API Quyền 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 vào các phông chữ đã cài đặt cục bộ, bao gồm cả thông tin chi tiết cấp cao hơn như tên, kiểu và bộ phông chữ, 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, 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ài đặt trên máy 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 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 Quyền truy cập phông chữ cục bộ hay không.
Trình bổ trợ 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ỉ có trong một thời gian ngắn thông qua các cờ Chrome thử nghiệm, nhưng một số sản phẩm 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 Local Font Access API 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 văn bản phần giữ chỗ thay vì 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. Lưu ý khối try…catch
cần thiết để xử lý đúng cách các lỗi về quyền.
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ữ cục bộ, hệ thống sẽ tạo một fontsIndex
đơn giản và chuẩn hoá từ danh sách đó:
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à được 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 điền cơ sở dữ liệu, tiện ích bộ chọn phông chữ có thể truy vấn cơ sở dữ liệu đó và hiển thị kết quả trên màn hình:
Điều đáng nói 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ữ để hiển thị trong một 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 rất phổ biến, giúp người dùng sử dụng phông chữ trên máy cho các thiết kế và tác phẩm sáng tạo của họ. Khi hình dạng API thay đổi và tính năng bị lỗi trong một thời gian ngắn, người dùng đã nhận thấy ngay lập tức. Jarosław đã nhanh chóng thay đổi mã thành mẫu phòng thủ mà bạn có thể thấy trong đoạn mã ở trên. Mẫu này hoạt động với Chrome đã cập nhật cũng như các dẫn xuất Chromium khác có thể chưa chuyển sang phiên bản mới nhất. Hãy dùng thử Boxy SVG và nhớ kiểm tra các phông chữ đã cài đặt trên máy. Bạn có thể khám phá một số phông chữ cổ điển đã bị lãng quên từ lâu như Zapf Dingbats hoặc Webdings.