Sử dụng kiểu chữ nâng cao với phông chữ trên máy

Tìm hiểu cách Local Font Access API cho phép bạn truy cập vào các phông chữ được cài đặt trên máy của người dùng và lấy thông tin chi tiết cấp thấp về các phông chữ đó

Phông chữ an toàn trên web

Nếu đã làm việc trong lĩnh vực phát triển web đủ lâu, có thể bạn sẽ nhớ đến cái gọi là phông chữ an toàn cho web. Những phông chữ này được biết là có trên hầu hết các phiên bản của những hệ điều hành được dùng nhiều nhất (cụ thể là Windows, macOS, các bản phân phối Linux phổ biến nhất, Android và iOS). Vào đầu những năm 2000, Microsoft thậm chí còn đi đầu trong một sáng kiến có tên là TrueType core fonts for the Web (Các phông chữ TrueType cốt lõi cho Web). Sáng kiến này cung cấp miễn phí các phông chữ này với mục tiêu là "bất cứ khi nào bạn truy cập vào một trang web chỉ định các phông chữ này, bạn sẽ thấy các trang chính xác như ý định của nhà thiết kế trang web". Có, điều này bao gồm cả những trang web sử dụng phông chữ Comic Sans MS. Dưới đây là một bộ phông chữ an toàn cổ điển trên web (với phương án dự phòng cuối cùng là bất kỳ phông chữ sans-serif nào) có thể trông như thế này:

body {
  font-family: Helvetica, Arial, sans-serif;
}

Phông chữ trên web

Thời mà phông chữ an toàn trên web thực sự quan trọng đã qua từ lâu. Ngày nay, chúng ta có phông chữ trên web, một số phông chữ thậm chí còn là phông chữ có thể thay đổi mà chúng ta có thể điều chỉnh thêm bằng cách thay đổi các giá trị cho nhiều trục được hiển thị. Bạn có thể sử dụng phông chữ trên web bằng cách khai báo một khối @font-face ở đầu CSS, trong đó chỉ định(các) tệp phông chữ cần tải xuống:

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: url('flamboyant.woff2');
}

Sau đó, bạn có thể sử dụng phông chữ web tuỳ chỉnh bằng cách chỉ định font-family như bình thường:

body {
  font-family: 'FlamboyantSansSerif';
}

Phông chữ trên máy dưới dạng vectơ vân tay

Hầu hết các phông chữ trên web đều đến từ web. Tuy nhiên, một điểm thú vị là thuộc tính src trong khai báo @font-face, ngoài hàm url(), cũng chấp nhận hàm local(). Điều này cho phép tải phông chữ tuỳ chỉnh (bất ngờ!) trên máy. Nếu người dùng đã cài đặt FlamboyantSansSerif trên hệ điều hành của họ, thì bản sao cục bộ sẽ được dùng thay vì được tải xuống:

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: local('FlamboyantSansSerif'), url('flamboyant.woff2');
}

Cách tiếp cận này cung cấp một cơ chế dự phòng hiệu quả, có khả năng tiết kiệm băng thông. Rất tiếc, trên Internet, chúng ta không thể có những điều tốt đẹp. Vấn đề với hàm local() là hàm này có thể bị lạm dụng để nhận dạng trình duyệt. Hoá ra, danh sách phông chữ mà người dùng đã cài đặt có thể nhận dạng khá chính xác. Nhiều công ty có phông chữ riêng của công ty được cài đặt trên máy tính xách tay của nhân viên. Ví dụ: Google có một phông chữ doanh nghiệp tên là Google Sans.

Ứng dụng Font Book của macOS cho thấy bản xem trước của phông chữ Google Sans.
Phông chữ Google Sans được cài đặt trên máy tính xách tay của một nhân viên Google.

Kẻ tấn công có thể cố gắng xác định công ty mà một người đang làm việc bằng cách kiểm tra sự tồn tại của một số lượng lớn các phông chữ doanh nghiệp đã biết như Google Sans. Kẻ tấn công sẽ cố gắng hiển thị bộ văn bản trong các phông chữ này trên canvas và đo lường các glyph. Nếu các ký tự khớp với hình dạng đã biết của phông chữ công ty, thì kẻ tấn công đã thành công. Nếu các glyph không khớp, kẻ tấn công sẽ biết rằng một phông chữ thay thế mặc định đã được sử dụng vì phông chữ của công ty chưa được cài đặt. Để biết đầy đủ thông tin chi tiết về cuộc tấn công này và các cuộc tấn công khác bằng dấu vân tay trình duyệt, hãy đọc bài khảo sát của Laperdix và cộng sự.

Ngoài phông chữ của công ty, ngay cả danh sách phông chữ đã cài đặt cũng có thể nhận dạng được. Tình hình liên quan đến vectơ tấn công này đã trở nên tồi tệ đến mức gần đây, nhóm WebKit đã quyết định "chỉ đưa vào [danh sách các phông chữ có sẵn] phông chữ web và phông chữ đi kèm với hệ điều hành, chứ không phải phông chữ do người dùng cài đặt cục bộ". (Và tôi ở đây, với một bài viết về việc cấp quyền truy cập vào phông chữ trên máy.)

Local Font Access API

Phần đầu của bài viết này có thể khiến bạn cảm thấy tiêu cực. Chúng ta không thể có những điều tốt đẹp sao? Đừng lo lắng. Chúng tôi nghĩ rằng chúng tôi có thể, và có lẽ mọi thứ đều không vô vọng. Nhưng trước tiên, hãy để tôi trả lời một câu hỏi mà có thể bạn đang tự hỏi.

Tại sao chúng ta cần API Local Font Access khi đã có phông chữ trên web?

Trước đây, các công cụ thiết kế và đồ hoạ chuyên nghiệp rất khó cung cấp trên web. Một trở ngại là việc không thể truy cập và sử dụng đầy đủ các loại phông chữ được tạo và gợi ý chuyên nghiệp mà nhà thiết kế đã cài đặt trên máy. Phông chữ web cho phép một số trường hợp sử dụng xuất bản, nhưng không cho phép truy cập theo cách có lập trình vào các hình dạng ký tự vectơ và bảng phông chữ mà trình chuyển đổi raster sử dụng để hiển thị đường viền ký tự. Tương tự, không có cách nào để truy cập vào dữ liệu nhị phân của phông chữ trên web.

  • Các công cụ thiết kế cần có quyền truy cập vào các byte phông chữ để triển khai bố cục OpenType của riêng chúng và cho phép các công cụ thiết kế kết nối ở cấp độ thấp hơn, cho các thao tác như thực hiện bộ lọc vectơ hoặc biến đổi trên các hình dạng glyph.
  • Nhà phát triển có thể có các ngăn xếp phông chữ cũ cho những ứng dụng mà họ đang đưa lên web. Để sử dụng các ngăn xếp này, bạn thường cần có quyền truy cập trực tiếp vào dữ liệu phông chữ, điều mà phông chữ trên web không cung cấp.
  • Một số phông chữ có thể không được cấp phép để phân phối trên web. Ví dụ: Linotype có giấy phép cho một số phông chữ chỉ bao gồm việc sử dụng trên máy tính.

Local Font Access API là một nỗ lực nhằm giải quyết những thách thức này. Mã này bao gồm 2 phần:

  • Một API liệt kê phông chữ, cho phép người dùng cấp quyền truy cập vào toàn bộ các phông chữ hệ thống hiện có.
  • Từ mỗi kết quả liệt kê, khả năng yêu cầu quyền truy cập vào vùng chứa SFNT cấp thấp (dựa trên byte) bao gồm toàn bộ dữ liệu phông chữ.

Hỗ trợ trình duyệt

Browser Support

  • Chrome: 103.
  • Edge: 103.
  • Firefox: not supported.
  • Safari: not supported.

Source

Cách sử dụng Local Font Access API

Phát hiện đối tượng

Để kiểm tra xem Local Font Access API có được hỗ trợ hay không, hãy sử dụng:

if ('queryLocalFonts' in window) {
  // The Local Font Access API is supported
}

Liệt kê phông chữ trên máy

Để lấy danh sách các phông chữ được cài đặt cục bộ, bạn cần gọi window.queryLocalFonts(). Lần đầu tiên, thao tác này sẽ kích hoạt một lời nhắc cấp quyền mà người dùng có thể phê duyệt hoặc từ chối. Nếu người dùng phê duyệt việc truy vấn phông chữ trên máy của họ, trình duyệt sẽ trả về một mảng có dữ liệu phông chữ mà bạn có thể lặp lại. Mỗi phông chữ được biểu thị dưới dạng một đối tượng FontData có các thuộc tính family (ví dụ: "Comic Sans MS"), fullName (ví dụ: "Comic Sans MS"), postscriptName (ví dụ: "ComicSansMS") và style (ví dụ: "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);
}

Nếu chỉ quan tâm đến một số phông chữ, bạn cũng có thể lọc các phông chữ đó dựa trên tên PostScript bằng cách thêm tham số postscriptNames.

const availableFonts = await window.queryLocalFonts({
  postscriptNames: ['Verdana', 'Verdana-Bold', 'Verdana-Italic'],
});

Truy cập vào dữ liệu SFNT

Bạn có thể truy cập đầy đủ vào SFNT thông qua phương thức blob() của đối tượng FontData. SFNT là một định dạng tệp phông chữ có thể chứa các phông chữ khác, chẳng hạn như phông chữ PostScript, TrueType, OpenType, Web Open Font Format (WOFF) và các phông chữ khác.

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

Bản minh hoạ

Bạn có thể xem Local Font Access API hoạt động trong bản minh hoạ. Đừng quên xem mã nguồn. Bản minh hoạ này giới thiệu một phần tử tuỳ chỉnh có tên là <font-select>, triển khai một bộ chọn phông chữ cục bộ.

Những điều cần cân nhắc về quyền riêng tư

Quyền "local-fonts" dường như cung cấp một nền tảng có khả năng nhận dạng vân tay cao. Tuy nhiên, người duyệt web có thể trả lại bất cứ nội dung nào họ muốn. Ví dụ: các trình duyệt tập trung vào tính ẩn danh có thể chọn chỉ cung cấp một bộ phông chữ mặc định được tích hợp vào trình duyệt. Tương tự, các trình duyệt không bắt buộc phải cung cấp dữ liệu bảng giống hệt như dữ liệu xuất hiện trên đĩa.

Bất cứ khi nào có thể, Local Font Access API được thiết kế để chỉ hiển thị chính xác thông tin cần thiết để bật các trường hợp sử dụng đã đề cập. Các API hệ thống có thể tạo ra một danh sách các phông chữ đã cài đặt không theo thứ tự ngẫu nhiên hoặc thứ tự sắp xếp, mà theo thứ tự cài đặt phông chữ. Việc trả về chính xác danh sách các phông chữ đã cài đặt do một API hệ thống như vậy cung cấp có thể làm lộ thêm dữ liệu có thể được dùng để nhận dạng vân tay và các trường hợp sử dụng mà chúng ta muốn bật sẽ không được hỗ trợ bằng cách giữ lại thứ tự này. Do đó, API này yêu cầu dữ liệu được trả về phải được sắp xếp trước khi trả về.

Tính bảo mật và quyền truy cập

Nhóm Chrome đã thiết kế và triển khai Local Font Access API theo các nguyên tắc cốt lõi được xác định trong Kiểm soát quyền truy cập vào các tính năng mạnh mẽ của nền tảng web, bao gồm quyền kiểm soát của người dùng, tính minh bạch và tính tiện dụng.

Quyền kiểm soát của người dùng

Người dùng có toàn quyền kiểm soát các phông chữ của mình và sẽ không được phép truy cập trừ phi họ cấp quyền "local-fonts" (như được liệt kê trong sổ đăng ký quyền).

Sự minh bạch

Bạn có thể xem liệu một trang web đã được cấp quyền truy cập vào phông chữ trên máy của người dùng hay chưa trong bảng thông tin trang web.

Quyền có tác dụng lâu dài

Quyền "local-fonts" sẽ được duy trì giữa các lần tải lại trang. Bạn có thể thu hồi quyền này thông qua trang thông tin trang web.

Phản hồi

Nhóm Chrome muốn biết trải nghiệm của bạn khi sử dụng Local Font Access API.

Hãy cho chúng tôi biết về thiết kế API

Có điều gì về API không hoạt động như bạn mong đợi không? Hoặc có phương thức hay thuộc tính nào bị thiếu mà bạn cần triển khai ý tưởng của mình không? Bạn có câu hỏi hoặc bình luận về mô hình bảo mật? Báo cáo vấn đề về quy cách trên kho lưu trữ GitHub tương ứng hoặc thêm ý kiến của bạn vào một vấn đề hiện có.

Báo cáo vấn đề về việc triển khai

Bạn có phát hiện thấy lỗi trong quá trình triển khai của Chrome không? Hoặc việc triển khai có khác với quy cách không? Báo cáo lỗi tại new.crbug.com. Nhớ cung cấp càng nhiều thông tin chi tiết càng tốt, hướng dẫn đơn giản để tái hiện và nhập Blink>Storage>FontAccess vào hộp Thành phần.

Thể hiện sự ủng hộ đối với API

Bạn có dự định sử dụng Local Font Access API không? Sự ủng hộ công khai của bạn giúp nhóm Chrome ưu tiên các tính năng và cho các nhà cung cấp trình duyệt khác thấy tầm quan trọng của việc hỗ trợ các tính năng này.

Gửi một tweet đến @ChromiumDev bằng thẻ bắt đầu bằng #LocalFontAccess và cho chúng tôi biết bạn đang sử dụng công cụ này ở đâu và như thế nào.

Lời cảm ơn

Thông số kỹ thuật Local Font Access API được chỉnh sửa bởi Emil A. Eklund, Alex Russell, Joshua BellOlivier Yiptong. Bài viết này được Joe Medley, Dominik RöttschesOlivier Yiptong xem xét. Hình ảnh chính của Brett Jordan trên Unsplash.