Bài viết kỹ thuật

Font chữ và Văn bản trong PDF: Lý do Glyph biến thành các Ô vuông

Một tệp PDF trông hoàn hảo trên máy của bạn và hiển thị dưới dạng một hàng các ô trống trên máy của người khác là lỗi font chữ phổ biến nhất trong phần mềm tài liệu và nó hầu như không bao giờ có nghĩa là văn bản bị sai. Các ký tự vẫn còn nguyên vẹn, bảng mã vẫn ổn, các glyph chỉ đơn giản là không có ở đó. Điều thay đổi giữa hai máy là các font chữ mà hệ điều hành đã cài đặt và khoảng cách giữa tệp có thể mang theo (portable) và tệp dễ vỡ (fragile) là một quyết định được đưa ra khi trang được viết: liệu font chữ có được gắn kèm bên trong tệp PDF hay được cho là có sẵn trên thiết bị đích

Để hiểu lý do tại sao điều đó xảy ra và tại sao một sự thất bại riêng biệt lại tạo ra văn bản trông có vẻ như có thể tìm kiếm được nhưng lại sao chép ra thành những dòng vô nghĩa, có nghĩa là phải xem xét cách PDF lưu trữ văn bản. Nó không lưu trữ câu. Nó lưu trữ các mã glyph cộng với một chương trình font chữ cộng với các bảng ánh xạ mã này với mã kia và mọi lỗi hiển thị hoặc trích xuất đều nằm trong khoảng trống giữa ba phần đó. Tiếp theo là một chuyến tham quan bộ máy đó, dựa trên ISO 32000, với các lệnh gọi Delphi điều khiển nó ở những nơi cần thiết

Ký tự, mã và glyph là ba điều khác nhau

Từ vựng khiến mọi người vấp ngã bởi vì lời nói hàng ngày gộp ba ý tưởng khác nhau vào từ "chữ cái". Một ký tự (character) là một đơn vị viết trừu tượng, ý tưởng về chữ A in hoa, được xác định trong Unicode là U+0041. Một glyph là một hình dạng được vẽ ra, đường nét với các đường cong và đoạn thẳng mà một font chữ cụ thể sử dụng để mô tả ký tự đó. Nằm giữa chúng là mã (code): byte hoặc các byte trong luồng nội dung cho trình xem biết glyph nào trong font chữ hiện tại cần vẽ

PDF hoạt động bằng mã. Khi một luồng nội dung hiển thị một chuỗi, các byte đó là các chỉ số trỏ vào font chữ đang hoạt động, không phải Unicode. Bảng mã của font chữ quyết định rằng mã 65 có nghĩa là "vẽ glyph được nộp dưới số 65" và không có gì trong thao tác đó biết được kết quả trông giống như chữ A đối với con người. Đó là điều làm cho PDF hiển thị giống hệt nhau ở mọi nơi nó có thể tìm thấy các glyph và đó cũng là lý do tại sao trích xuất là một vấn đề tách biệt với hiển thị: vẽ chỉ cần ánh xạ từ mã-sang-glyph, đọc cần ánh xạ từ mã-sang-Unicode và đó là hai bảng khác nhau có thể mâu thuẫn hoặc mất đi một cách độc lập

Các loại font chữ bạn thực sự sẽ gặp

ISO 32000 xác định một số loại từ điển font chữ và trên thực tế, một tài liệu bạn nhận hoặc tạo ra sử dụng một trong ba loại này. Biết bạn đang xem loại nào giải thích hầu hết những gì có thể sai sót

Type 1 là định dạng đường nét PostScript gốc của Adobe, được xây dựng từ các đường cong Bezier khối (cubic Bezier curves). Mười bốn font chữ tiêu chuẩn mà mọi trình đọc tuân thủ đều phải cung cấp, các họ Helvetica, Times, Courier, Symbol và ZapfDingbats, là Type 1 và một từ điển font chữ gọi tên một trong số chúng có thể bỏ qua chương trình font chữ một cách hợp pháp. Đó là trường hợp duy nhất mà việc để lại font chữ không nhúng (unembedded) là an toàn theo đặc tả kỹ thuật chứ không phải do may mắn. Đối với bất kỳ kiểu chữ Type 1 nào khác, chương trình phải được nhúng, hoặc trình xem sẽ thay thế bằng một font chữ thường tương tự về số liệu nhưng khác biệt về mặt hiển thị

TrueType sử dụng các đường cong bậc hai và bắt nguồn từ thế giới của Apple và Microsoft. Đây là loại font chữ hệ thống phổ biến nhất và là loại bạn sẽ nhúng thường xuyên nhất. Một font chữ TrueType đơn giản trong PDF bị giới hạn ở các mã byte đơn, vì vậy một font chữ như vậy có thể phân xử nhiều nhất 256 glyph cùng một lúc. Mức trần đó là lý do cấu trúc khiến CJK và các bảng chữ cái lớn khác không thể nằm trên một font chữ đơn giản

Type 0, font chữ ghép hoặc dùng mã CID, là câu trả lời cho giới hạn đó. Nó sử dụng mã nhiều byte và một CMap để định tuyến chúng thông qua một CIDFont phái sinh, mà các đường nét của chính nó là TrueType hoặc CFF/Type 1. Đây là loại font chữ duy nhất có thể chứa hàng ngàn glyph, do đó bất kỳ tệp PDF nào có chứa tiếng Trung, tiếng Nhật, tiếng Hàn hoặc danh sách đa ngôn ngữ rộng rãi đều sử dụng Type 0 cho dù tác giả có nghĩ đến nó hay không. Sự đánh đổi là tính phức tạp: nhiều bộ phận chuyển động hơn, đòi hỏi nhiều bộ phận trong số đó phải chính xác cho cả việc hiển thị và trích xuất

Một font chữ TrueType được hiển thị ở các điểm 12, 18, 24 và 36 trong PDF, cho thấy một đường viền nhúng duy nhất thu phóng đến bất kỳ kích thước nào

Một chi tiết đằng sau bức ảnh đó thúc đẩy kích thước tệp. Một font chữ là một thư viện các đường nét phác họa, không phải bitmap kích thước cố định, do vậy một chương trình nhúng sẽ phục vụ mọi kích thước điểm trên trang. Việc thu phóng chỉ là phép chuyển đổi diễn ra lúc vẽ, đó là lý do mà tiêu đề chữ với mảng văn bản bên dưới đều chia sẻ chung một kiểu chữ được nhúng và vì sao chi phí để nhúng là cho mỗi font chữ thay vì là cho mỗi kích thước

Nhúng (Embedding) là điểm khác biệt giữa di động linh hoạt (portable) và mong manh dễ vỡ

Nhúng ở đây nghĩa là chương trình font chữ, thông tin đường viền thực sự, được ghi xuống tập tin PDF thành một luồng dữ liệu (stream). Một trình đọc (reader) trên một cái máy chưa bao giờ biết qua font chữ này của bạn sẽ đọc các đường phác họa đó ngay từ trong tệp tin và vẽ chính xác từng mẫu ký tự (glyph). Bỏ qua thao tác nhúng thì tức là bạn đang cược thiết bị đích có cùng font chữ; nếu lỡ mà không có, thì bộ trình xem này sẽ nhảy xuống sử dụng font khác. Đối với mười bốn phông chữ tiêu chuẩn, sự chuyển này đã được định sẵn và an lành (benign). Tuy nhiên, đối với các loại khác, thì việc thay font chữ này dao động qua việc chữ nhìn gần giống bằng mặt font khác cho tới lúc bị vỡ hình thành ô trống (empty-box) bởi lúc này chẳng có bất kỳ loại thay thế nào có khả năng bao phủ trọn gói loại chữ của font đó cả

Với HotPDF phần hiệu khiển là ở chỉ một tính năng riêng lẻ duy nhất, được xếp dọn trước lúc tệp tin mở lối ra. FontEmbedding ra lệnh tới hệ thống đóng gói các bề mặt chữ nó vẽ được lót sẵn vào tài liệu:

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'report.pdf';
    Pdf.Compression := cmFlateDecode;
    Pdf.FontEmbedding := True;          // outlines travel inside the file
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Calibri', [], 11);
    Pdf.CurrentPage.TextOut(72, 760, 0, 'This renders the same on a machine without Calibri.');
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Trình tự không mang tính mỹ phẩm, nó không phải là sự tùy ý. BeginDoc là khi HotPDF định đoạt, quyết định cấu trúc của văn bản, từ đó FontEmbedding tất phải hiện hữu mang đúng thực (true) kể trước khi có lần hiệu triệu. Ráp dính chúng đằng sau chuyện này không hề sinh ra bất kỳ nhầm lẫn (error), cũng chả tồn cảnh báo nào cả, đơn thuần chỉ xuất tệp ra bên ngoài bình thản âm thầm trong trạng thái chẳng mang theophong chữ. Loại trục trặc rợn gáy ác báo (worst) thế này: trượt luồn mọi thủ đoạn test lúc đang thử tay (developer's machine) với kiểu font chữ tình cờ nằm đâu đó sẵn rồi, xong hiển hiện bùng cháy phanh phui trước bàn phím của khách hàng khi ở cỗ máy chưa từng có bóng dáng chữ đó

Nhúng cũng là chuyện ranh giới tiếp điểm đụng của kỹ thuật chế tác và nguyên tắc được ủy cấp (licensing). Một hệ trình font mang các mã đánh hiệu chỉ ra có hoặc không việc cho phép nhúng phóng khoáng không ràng buộc (freely), cho trích lục xem thử hay hoàn toàn miễn nhiễm bị phong toả. Tuân kính các lằn ranh hiệu cấp đó quy chiếu phận sự của bạn chứ đừng nhầm người họa công, mà nói theo một cách "nó dùng được" khác trọn toàn vẹn mặt kia là "nó được luật cấp quyền."

Chắt lọc (Subsetting): chỉ nhúng những mẫu chữ (glyphs) bạn có dùng

Bộ nhúng dạng tràn (Full embedding) dội rót toàn bộ chương trình phần font xuống tới trong file. Bản CJK kích lớn thuộc bộ họ TrueType có thể cồng cềnh ngốn bự vài byte tính theo megabyte, mà vác luôn dạng nguyên trạng như thế hòng đánh ra được độ một chục cái ký tự thật hao của kiểu phá hoại, mà lại còn cộng gộp nhân đa đối với một tệp mang cả hàng tá trang nữa chứ. Kiểu ngắt (subsetting) gỡ cái khúc mắc ấy bởi duy việc ghi những loại ký tự thực tại mà có vết lưu trú của tập văn, rối nó mới mượn luôn hình đổi dạng nhãn của font lấy đoạn thẻ 6 vần đánh thêm dấu ghép chữ thập (+), dạng nọ thuộc ABCDEF+Calibri trong bản trích lục nhấp nháy cho tệp ngắt nhỏ bất kỳ nào (subsetted), giúp một anh bạn coi sách (reader) chả thuở nào xáo trộn đục lẫn đi bảng mảng font xén (partial face) lại đè nhầm dòng font bọc hoàn bị ở hệ thống (full system font) dù hai chú cùng đeo chung bảng định danh (same name)

Chiếu ở các tập tài liệu kiến sinh thì ngắt con chia nhánh đó là con bài đúng chuẩn ở cơ bản (right default). Nó léo co kích cở đi liền cân ứng theo dòng dung thể, đối ngịch với phần gốc (source font), tác động rất là lớn cho dàn bản có phần tiếng nói quá rộng với những khối font rỗng nặng kia, có chăng sẽ thao túng trọn cả bộ cấu trúc. Lưu luyến mà không nhắc một dặn dò đó chính thế hệ con được cắt ra chỉ ấp chứa những loại đồ từng xài vào thời mốc nhào đúc mà thôi. Giả mà như cái vòi quy trình chạy bên dưới (downstream process) ráng nắn nót mớ text phụ nạp cho mảng nhắt ấy đi, lũ chữ dạng hình nó truy cầu có vể không tồn vong trong tập, biến chính nó môt cái còng chân ở thao tác ghim sửa thêm thắt sau này nơi con đẻ của kẻ ngoài

Các font chữ Unicode với tai vạ những hố hộp ở CJK

Lúc dòng thoại rời dải bảng ký Latin bộc lạch thuần thì bộ nẻo cho font gọn nhẹ khép cánh ngõ, và phần ván bôi trát thì chỉ định gọi hiệu trổ dải bao ôm khả lực phân hoán của kiểu Unicode thẳng mặt xong bảo HotPDF chắp cấu kiểu chữ Type 0 bới từ đống đó. Phân RegisterUnicodeTTF thồ rinh tệp loại TrueType qua kênh lộ đường đi; sau chuỗi đó mặt loại mới đánh ký gửi được trưng xuất hữu dụng nằm ngay sau câu SetFont chẳng khác những thể bình dân:

Pdf.FontEmbedding := True;
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansCJKsc-Regular.ttf');
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('NotoSansCJKsc-Regular', [], 14);
Pdf.CurrentPage.TextOut(72, 720, 0, '你好,世界 こんにちは 안녕하세요');
Pdf.EndDoc;

Hai mảng điểm quyết liệt duy trì hoặc sập hệ. Chiếc bộ phải phủ chùm được bộ vạch ký (scripts) dãi chuỗi câu lệnh: dạng loại TrueType rành rẽ mỗi tiếng Latin nhất quyết chẳng cho nảy thêm cái ngấn cạch Trung (Chinese) nào mọc trồi lên đâu, thế nên kết điểm cũng trở chát trút ra một ô khoang chữ (boxes) chỏng không y vậy một phen mới, chuyến này là tại mớ hình dấu vết đó thực thụ chẳng góp ngự sinh sống chỗ mặt font chữ ý. Hơn thế nhúng mã font (embedding) hẵn phải lưu dính trên nấc On (stay on), do tại nòng tảng Type 0 có thắt ráp tự thân con số TTF đã ký ấn rồi thì chả bõ ngấm tới loại trình xem sách mảy chẳng dò nhấp trúng loại rãnh viền ở ngoài mô. Ánh ở mảng dung thể bị pha trộn rắc rối sự bền thế tuyển là thể bệ phủ ngợp rộng lớn, dải bộ chi tộc Noto và dạng Arial Unicode MS thì luôn sắn đứng đài vinh bục đón tiếng xưng danh (usual answers), đã thế lại được cho đi dạng mảng nhúng bọc nguyên gốc và đánh chắt tách khối

Định trình quy hất đi phải sang rẽ hướng chiều qua trái cùng với mấy bảng điệu hình đan lồng ghép nối bện cộm đắp vướng lên trên nếp bao mặt ngoài nữa. Phía HotPDF chìa mâm nài đón hàm vòi RtLTextOut dành phả phân dải tiếng Ả Rập chung mặt với ngôn xưng Hebrew nọ, bọc choảng khâu đổi chuyển nắn vòng phân vị lật tráo đặng bạn chỉ thẩy nhét đúng chuỗi đi phân mạch luận định thẳng để cho mặt dàn khung (layout) đó cái đám viện này gánh. Kéo dải cho trơn Ả Rập nhịp nhàng quy chụp là độ quy lấp phủ cùng nét gò dáng tạc kết xen cả độ dòng chạy xuôi đảo, ba cụm nẻo đường bửa rời ra đó, có bõ hiện mỗi khung hình cục hộp đúc nơi bãi đó tức thì thảy báo ngay rớt hư bất kỳ cụm trong ba mớ bòng bong này

Cái bàn bản vị ToUnicode: Dinh thự rải ngã rẽ copy dán tay

Tất thảy thứ ở thượng tầng là nỗi lo bề mặt đánh vẽ. Khoản trích dẫn là bóng gương đảo lật tráo và nó sụp ngã do vì duyên nghiệp riêng biệt. Một gã trình duyệt bôi dặm tô quét trang đi dùng sơ đồ biến đổi ánh xạ mã biến chữ mác hình (code-to-glyph) bằng loại của font chữ đó, nhưng khi 1 gã sử đồ quét lấy nét khối rồi vớt chụp cóp mang ra tay, trình hệ coi sách này nọ cần móc xới những loại khoá đó trả tọc ngược về loại của dạng Unicode. Việc hoán chuyển xoay nghịch kia đây gọi nôm là bảng ánh ToUnicode CMap, là tuyến dòng chảy tuỳ tuyển cắm đi vào cái nhánh phông mặt đó

Lúc nó tỏ lộ hữu hiện và ngay chính, phần text bị chộp vớt bứng vọt mang đúng nhân hiệu nếp ký vạch dáng điệu. Khá xui mà nó mất hút hoặc trượt đường sai hỏng, hay là do mảng phông bị tỉa rành ngắt dóng mang nhãn tự đánh cho riêng mác hình còn mã mảng đánh hiệu ToUnicode tuyệt chẳng thấy được phác phẩy nặn vẽ bôi lên, mảnh tài liệu trang ngoài hiện rạng y như mượt sắc còn khay gánh vác khay hứng rác của vùng nhớ lóng ngóng gom toàn thảy đồ hư bỏ vô tri (garbage): đồ nẻo mã dạng bộ glyph được phân chiếu luận ngỡ như nó gốc từ mẻ Unicode thật, và sự thật thì với đồ tạc nặn quy ngắt dóng ấy nó bấy cũng chẳng hẵng trọn vẹn đúng y được nốt nữa. Chuyện cũng đó là cắt nghĩa làm sao mảnh tài vớt dạng soi màng đánh chụp ảnh bọc kèm đồ quy nẻo text ở loại chữ ký trổ OCR (OCR text layer) cho đào vớt bứng rà moi móc mà tệp nguyên nảy phôi mầm bản PDF nặn do mấy cỗ đồ xuất quăng quật thì không ra hồn được. Chỉnh hình và mảng rẽ vớt tách chữ đào dẫn tới hệ đồ mâm chiếu lệch pha, ra làm vậy có cớ để cho trang tin trọn đẹp thoả 1 phương cũng dội trật đổ sụm về ngả kia được. Giả sử việc vớt rẽ lẩy nội dung hệ trọng trong mặt vác xuất bản phần ra của ngài, ghim nạp thứ gọi vạch mâm rọi chiếu mã ToUnicode cho hẳn phải y nguyên (correct) đóng cứng cho như lệnh thiết thực sinh nhai, rồi bắt gỡ chọc ra tra thẩm soát thông vòng việc giựt cóp bứng text từ tròng dóng mẫu xem ra đặng mới trao gửi chữ tín tin thác nó yên bệ ngự đó chăng

Đoán định hư hỏng do phông tức thì cách nào

Mảng loại vướng sai lầm ngầm báo về hướng để bạn trổ mắt soi kiếm. Những cụm hộp lóng ngóng gánh vác rỗng đúc ở các vựa bộ đồ máy kia thường quy ngọn quy do tạc ngụ vì bởi một loại dáng font tuyệt không bị ngự gài móc kẹp ẩn chung vào trọn bộ tệp tài văn bản, gởi vậy vạch soi mục gài ẩn nhúng gộp mã font kia lúc ưu tiến hạng một cùng bộ cho toả trổ dải bề phơi phủ hình dạng font mẫu thứ tiếp. Khối rỗng (Boxes) mà còn nổi cộm lên hiện lồ lộ dẫu tại vùng sào hệ tư bộ tư nhân tự quản bản thân bạn mượn chỉ ngón chỉa ngay nốt vào hệ phủ hình (coverage): mảng font vốn chẳng gậm ôm kẹp cái vùng hệ cấu tạo văn loại kia, khỏi kể có ẩn trốn cài luồn trong bản chi cả (embedding). Lời thoại câu dải nắn lộ bôi hình xuất trôi tròn trọt mà rước móc copy vác thẩy ra như mấy nốt trống không nghĩa (nonsense) trọc quăng đi trúng là ca sụm giò sụp mương thuộc vấn phần lỗi rớt ngay rãnh ToUnicode mà chả nằm nơi ngã bệnh kết hình tạo mã trổ bôi trát đâu, ráng ngúng ngoẩy giằng co cào bới nghịch trò font phông đi hoặc ẩn mảng ghim nhúng gộp thảy vẫn bất thành cải tạo chữa khỏi do nẻo vẽ phác có hư rách gì đâu từ dạo cũ. Hòng phán trọn được phần thông văn thành tệp xuất chi rồi kia, bóc tung trong hệ trình Acrobat mà kiếm soi ghim mắt tới khung Thuộc phân Cấu trúc Document (Document Properties), tại mục Nhánh Fonts: ở dóng vạch rạng mạnh có xuất lòi tỏ chủng mặt phông ra, kêu gào rõ mác ngự nhúng (Embedded) dẫu ngự kiểu lẩy (Embedded Subset) kèm chép gọi trổ danh bộ nhãn đóng mã (encoding). 1 phông mảng phông ghim lệnh đáng bị lấp nhúng gộp vô trọn lại lóng chỏng mảng bơ phờ lộ tuột mặt tự kêu than thú rành tội vạ bóc trần ra cớ sự trổ ở góc nọ mãi tận hồi lăng xăng khách mảng ngộ giác hớ rạng trước chót

Mớ lăng nhăng bòng bong đây lại ra là thâm kì ngoại cảnh hết dạo lúc rã rẽ mảng điểm chẽ hất chia ký rẽ (character), đoạn quy định mốc (code), lẫn dải khuôn hình vạch hình (glyph) trong vắt thâu tấu tường tận. Luồn ghim hệ mảng phông mà người đã trổ vẽ cùng chung, cấu gãy móc rời dải phông sồ to nạc (subset), rướn gợn tay túm thấu lẩy ngay bộ diện khuôn trổ Unicode và giáng gọi nạp vào RegisterUnicodeTTF cái giây ngay khoảnh mà dải thoại rời bãi mảng hệ Latin kia ra, đặng kèm ôm ghì riết chặt một bộ trổ quy mảng rọi soi mặt phản ánh vạch đi ToUnicode rành mạch đúng y lỡ mà gặp có kẻ cần dở moi móc (extract) nẻo bới móc chữ ra. Ẵm trọn gọn gàng trúng phóc cái nhóm đi thì chả hộp ô xéo vuông gãy rỗng nào ngóc thò hiện ló diện tiếp đâu nữa. Đối phần mảng máy cơ móc vây bao quanh ốp bọc kia, nhóm mục bộ mổ xẻ một văn tệp PDF tinh thu gọn nhẹ thảnh thơi trỏ dẫn ngụ về cái chốn bộ tự mục mảng phông chiếm cư rớt rụng tại trên cành rẽ cây cấu phân đối vạch đối tựa vạch ngang kia (object tree), còn khoản mục phân dạo lướt qua dải kiến trúc văn tệp (the document structure walkthrough) luồn bao gánh phả qua mảng phần nguồn dải kho đắp sẻ san chắp vá xuyên chạy rạch ra khỏi các bờ bãi trang

Mảng lẩy nạp hiệu SetFont, nhúng ghì FontEmbedding, cùng cả RegisterUnicodeTTF bộc chiếu phơi mặt lên tại nẻo này gộp là một nẻo góc thuộc của bộ chi gốc linh thư HotPDF Component tạo ngỏ hướng phần người nhồi nắn Delphi kèm rẽ nhánh chắp cả bên mặt C++Builder nữa