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

Tìm hiểu cách API Local Font Access cho phép bạn truy cập vào 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ề phông chữ đó

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

Nếu đã phát triển web đủ lâu, có thể bạn còn nhớ thứ gọi là phông chữ an toàn trên web. Các phông chữ này được biết là có trên hầu hết phiên bản của các 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í đã dẫn dắt một sáng kiến có tên là Phông chữ cốt lõi TrueType cho web. Chương trình này cung cấp những phông chữ này cho người dùng tải xuống miễn phí với mục tiêu "bất cứ khi nào bạn truy cập một trang web chỉ định các phông chữ này, bạn sẽ thấy các trang đúng như ý định của nhà thiết kế trang web". Có, bao gồm các trang web được thiết lập trong Comic Sans MS. Dưới đây là ngăn xếp phông chữ an toàn trên web cổ điển (với tính năng dự phòng tối ưu cho mọi phông chữ sans-serif) có thể có dạng như sau:

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

Phông chữ trên web

Những ngày mà phông chữ an toàn trên web thực sự quan trọng đã qua lâu rồi. Hiện nay, chúng ta có các phông chữ trên web, một số phông chữ trong số đó thậm chí là phông chữ biến mà chúng ta có thể điều chỉnh thêm bằng cách thay đổi giá trị cho các trục hiển thị khác nhau. Bạn có thể sử dụng phông chữ trên web bằng cách khai báo khối @font-face ở đầu CSS. Khối này 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 phông chữ trên web đều đến từ web. Tuy nhiên, một thực tế thú vị là thuộc tính src trong phần khai báo @font-face, ngoài hàm url(), còn chấp nhận hàm local(). Việc này cho phép hệ thống tải phông chữ tuỳ chỉnh một cách cục bộ (bất ngờ!) 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 sử dụng thay vì tải xuống:

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

Phương pháp này cung cấp một cơ chế dự phòng hiệu quả có thể giúp tiết kiệm băng thông. Trên Internet, rất tiếc là 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ị sử dụng sai mục đích khi tạo vân tay số trong trình duyệt. Hoá ra, danh sách phông chữ mà người dùng đã cài đặt có thể khá giống nhau. 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ó phông chữ công ty có tên Google Sans.

Ứng dụng macOS Font Book cho thấy bản xem trước 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 nhân viên Google.

Kẻ tấn công có thể cố gắng xác định công ty mà ai đó làm việc bằng cách kiểm thử sự tồn tại của một số lượng lớn phông chữ công ty đã biết như Google Sans. Kẻ tấn công sẽ cố gắng kết xuất văn bản được đặt trong các phông chữ này trên một canvas và đo lường các ký tự. Nếu các ký tự khớp với hình dạng đã biết của phông chữ hợp nhất, thì kẻ tấn công sẽ có một lượt truy cập. Nếu các ký tự không khớp, kẻ tấn công sẽ biết rằng 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 toàn bộ thông tin chi tiết về cuộc tấn công này và các cuộc tấn công tạo vân tay số khác trên trình duyệt, hãy đọc bài khảo sát này của Laperdix et al.

Khác biệt với phông chữ của công ty, thậm chí chỉ cần danh sách phông chữ đã cài đặt là có thể xác định được. Tình huống với vectơ tấn công này trở nên tệ đến mức gần đây, nhóm WebKit đã quyết định "chỉ bao gồm [trong danh sách phông chữ có sẵn] phông chữ và phông chữ trên web đi kèm với hệ điều hành, nhưng không bao gồm phông chữ do người dùng cài đặt cục bộ". (Tôi xin đọc một bài viết về cách cấp quyền truy cập vào phông chữ trên máy.)

API truy cập phông chữ cục bộ

Có thể phần mở đầu của bài viết này khiến bạn cảm thấy tiêu cực. Có thể chúng ta thực sự không có những điều tốt đẹp không? Đừng lo. Chúng tôi nghĩ mình có thể và có lẽ mọi thứ vẫn là 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 thắc mắc.

Tại sao chúng ta cần API Truy cập phông chữ cục bộ khi có phông chữ trên web?

Trước đây, việc phân phối các công cụ đồ hoạ và thiết kế có chất lượng chuyên nghiệp trên web rất khó. Một trở ngại là họ không thể truy cập và sử dụng toàn bộ phông chữ được tạo và gợi ý chuyên nghiệp mà các nhà thiết kế đã cài đặt trên máy. Phông chữ trên 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 quyền truy cập 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 tạo đường quét sử dụng để hiển thị các đường viền của ký tự. Tương tự như vậy, không có cách nào để truy cập vào dữ liệu nhị phân của phông chữ 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ữ để tự triển khai bố cục OpenType và cho phép các công cụ thiết kế truy cập ở các cấp thấp hơn, cho các hành động như thực hiện bộ lọc vectơ hoặc biến đổi trên các hình dạng ký tự.
  • Nhà phát triển có thể có ngăn xếp phông chữ cũ cho ứng dụng của họ mà họ đang đưa lên web. Để sử dụng các ngăn xếp này, chúng thường yêu cầu quyền truy cập trực tiếp vào dữ liệu phông chữ, một thứ 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 để gửi qua web. Ví dụ: Linotype có giấy phép cho một số phông chữ chỉ sử dụng cho máy tính.

Local Font Access API (API Truy cập phông chữ cục bộ) là một nỗ lực nhằm giải quyết những thách thức này. Hộp cát về quyền riêng tư bao gồm 2 phần:

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

Hỗ trợ trình duyệt

Hỗ trợ trình duyệt

  • 103
  • 103
  • x
  • x

Nguồn

Cách sử dụng API truy cập phông chữ cục bộ

Phát hiện tính năng

Để kiểm tra xem liệu Local Font Access API có được hỗ trợ hay không, hãy 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 phông chữ được cài đặt trên máy, bạn cần gọi window.queryLocalFonts(). Lần đầu tiên, thao tác này sẽ kích hoạ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 phông chữ trên máy của họ cần truy vấn, thì 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 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, Định dạng phông chữ mở web (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ể thấy Local Font Access API hoạt động trong bản minh hoạ dưới đây. Hãy nhớ xem cả 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 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" có vẻ như cung cấp một bề mặt có khả năng kiểm tra vân tay số cao. Tuy nhiên, trình duyệt có thể trả về mọi dữ liệu 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 sẵn trong trình duyệt. Tương tự, trình duyệt không bắt buộc phải cung cấp dữ liệu bảng chính xác như 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 nhằm hỗ trợ các trường hợp sử dụng đã đề cập. API hệ thống có thể tạo danh sách phông chữ đã cài đặt không theo thứ tự ngẫu nhiên hoặc được 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 phông chữ đã cài đặt do API hệ thống cung cấp có thể làm lộ thêm dữ liệu có thể được dùng để tạo vân tay số, đồng thời các trường hợp sử dụng 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 phải sắp xếp dữ liệu được trả về trước khi được trả về.

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

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 nêu trong bài viết Kiểm soát quyền truy cập vào các tính năng của nền tảng web mạnh mẽ, bao gồm cả quyền kiểm soát của người dùng, độ trong suốt và hiệu quả.

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

Quyền truy cập vào phông chữ của người dùng hoàn toàn thuộc quyền kiểm soát của người dùng và sẽ không được cấp quyền này trừ phi người dùng cấp quyền "local-fonts", như có tên trong danh sách quyền.

Sự minh bạch

Việc liệu 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 sẽ xuất hiện trong trang thông tin trang web.

Khả năng lưu trữ cố định quyền

Quyền "local-fonts" sẽ vẫn tồn tại giữa các lần tải lại trang. Bạn có thể thu hồi thông tin này qua trang tính thông tin trang web.

Ý kiến phản hồi

Nhóm Chrome muốn biết trải nghiệm của bạn với Local Font Access API.

Cho chúng tôi biết về thiết kế của API

Có điều gì về API không hoạt động như bạn mong đợi không? Hay có thiếu phương thức hoặc thuộc tính nào mà bạn cần triển khai không? Bạn có thắc mắc hoặc nhận xét về mô hình bảo mật? Gửi vấn đề về thông số kỹ thuật trên kho lưu trữ GitHub tương ứng hoặc thêm ý kiến của bạn vào vấn đề hiện có.

Báo cáo sự cố với quá trình triển khai

Bạn có phát hiện thấy lỗi khi triển khai Chrome không? Hay cách triển khai có khác với quy cách không? Gửi lỗi tại new.crbug.com. Hãy nhớ cung cấp nhiều thông tin chi tiết nhất có thể, hướng dẫn đơn giản để tái tạo và nhập Blink>Storage>FontAccess vào hộp Thành phần. Sự cố rất hữu ích trong việc chia sẻ các bản sao nhanh và dễ dàng.

Hỗ trợ API

Bạn có định sử dụng Local Font Access API không? Sự hỗ trợ 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 biết tầm quan trọng của việc hỗ trợ các tính năng đó.

Gửi một bài đăng trên Twitter đến @ChromiumDev kèm theo hashtag #LocalFontAccess và cho chúng tôi biết vị trí cũng như cách bạn sử dụng bài đăng này.

Xác nhận

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