Đặt một cuốn tiểu thuyết tiếng Nhật lên trang và điều đầu tiên bạn nhận ra là chữ chạy dọc theo từng cột, không vắt ngang theo dòng, và các cột tiến từ lề phải của tờ giấy dần sang bên trái. Một độc giả được nuôi dưỡng trong môi trường đó sẽ thấy văn bản ngang có phần hơi khô khan. Vấn đề kỹ thuật là PDF, cũng giống như hầu hết các hệ thống văn bản kỹ thuật số khác, được xây dựng dựa trên một baseline (đường cơ sở) ngang mọc dần từ trái sang phải, và một content stream không có khái niệm "hãy viết đoạn này gập xuống thay vì vắt ngang." Do đó khi một ứng dụng Delphi phải tạo chứng chỉ, bài thơ, bảng chỉ dẫn, hoặc một tài liệu pháp lý định dạng truyền thống cho độc giả Đài Loan, Nhật Bản hoặc Hàn Quốc, bố cục (layout) phải được rắp nối thủ công: ký tự này nằm dưới ký tự trước, cột này nằm sang bên trái của cột kia
HotPDF cung cấp cho bạn một công tắc để thực hiện phần công việc quản lý từng ký tự thay bạn. Font chữ mà bạn thiết lập mang một cờ (flag) IsVertical, và một khi cờ này được bật, chỉ với một lệnh gọi TextOut nó sẽ xếp chồng toàn bộ một chuỗi thành một cột dọc thay vì chạy dài trên một baseline. Phần còn lại của bài viết này sẽ giải quyết các vấn đề liên quan tới việc sắp xếp cột, thứ tự từ phải qua trái, và một việc thay thế ký tự tưởng như tĩnh lặng nhưng cực kì quan trọng

Công tắc này nằm tại hàm SetFont
Bố cục theo chiều dọc không phải là thuộc tính của một trang hoặc một tài liệu. Nó là thuộc tính của font chữ bạn dùng để vẽ, và bạn bật nó bằng tham số thứ năm của hàm SetFont:
// SetFont(FontName, FontStyle, Size, FontCharset, IsVertical)
// The 5th argument flips the current font into vertical mode.
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, DEFAULT_CHARSET, False); // horizontal
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, DEFAULT_CHARSET, True); // vertical
Bởi cờ này được gán lên font chữ nên bạn có thể chuyển qua lại giữa chế độ viết dọc và viết ngang đơn giản bằng cách gọi hàm SetFont lần nữa và thay tham số cuối cùng. Một trang có thể chứa một tiêu đề ngang ngang trên đỉnh trang và phần văn bản thân dọc ở bên dưới, và HotPDF giữ tách biệt hai chế độ theo từng đối tượng font chữ chứ không phải theo từng trang. Đó là điều giúp làm nên các bố cục pha trộn mà không yêu cầu bạn phải tự quản lý bất cứ trạng thái đặc biệt nào: bất cứ hàm TextOut nào xuất hiện sau hàm SetFont dọc sẽ xếp chồng thành khối, bất cứ hàm TextOut nào đi theo sau hàm ngang sẽ bám theo đường cơ sở, và cấu hình sẽ luôn bám theo lệnh SetFont cuối cùng
Tham số thứ tư là bộ mã (character set) của Windows, đây cũng là tham số giống với hàm khi gọi ở cấu hình ngang. Cung cấp DEFAULT_CHARSET sẽ để cho hệ thống xác định các ký tự tùy vào từng chuỗi; điều này có giá trị vì các tài liệu theo dạng dọc thường kết hợp các script nhiều ngôn ngữ. Mọi vấn đề khác về font chữ vẫn như cũ: nó bắt buộc phải cài đặt trên cái máy build phần mềm, và gần như lúc nào bạn cũng cần FontEmbedding := True để file đó có thể render y hệt các ký tự CJK trên cái máy xem tài liệu dù nó chưa từng có Arial Unicode MS
Mỗi TextOut ứng với một cột
Khi sử dụng font dọc, hàm TextOut không còn phân bố trải rộng chuỗi ký tự sang hai bên từ tọa độ định sẵn. Nó đóng mốc ký tự đầu tiên ở trên đỉnh cột rồi cho những ký tự còn lại dấn thẳng xuống dưới, bước tiến theo độ cao dòng của font cho mỗi ký tự. Tọa độ X bạn truyền sẽ neo cứng phần cột dọc đó; tọa độ Y bạn nhập sẽ cắm cố định điểm xuất phát dòng đầu trên đỉnh của cái cột đó. Vì vậy, để xây dựng một văn bản có bố cục thực thụ, bạn phát ra một câu lệnh TextOut cho mỗi cột và bước theo phương ngang qua trái với các hàm sau đó, vì các cột CJK được đọc từ phải sang trái
var
Pdf: THotPDF;
const
ColTop = 760; // y of the first glyph in every column (points)
ColGap = 28; // horizontal distance between columns
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'VerticalText.pdf';
Pdf.FontEmbedding := True; // embed the CJK face for portable rendering
Pdf.BeginDoc;
Pdf.CurrentPage.Size := psA4;
// A horizontal heading first, in the ordinary writing mode.
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 16, DEFAULT_CHARSET, False);
Pdf.CurrentPage.TextOut(60, 800, 0, 'Tang poem, vertical layout');
// Switch the font into vertical mode; every TextOut below now stacks.
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 18, DEFAULT_CHARSET, True);
// Columns advance right to left, so X decreases with each call.
Pdf.CurrentPage.TextOut(520, ColTop, 0, '床前明月光');
Pdf.CurrentPage.TextOut(520 - ColGap, ColTop, 0, '疑是地上霜');
Pdf.CurrentPage.TextOut(520 - ColGap * 2, ColTop, 0, '舉頭望明月');
Pdf.CurrentPage.TextOut(520 - ColGap * 3, ColTop, 0, '低頭思故鄉');
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Bài thơ đọc đúng như dự định: cột ngoài cùng bên phải chạy trước, từ trên xuống dưới, rồi sau đó mắt nhìn đảo sang bên trái đến cột kế tiếp. Bản thân cái chuỗi văn bản chả mã hóa tý gì liên quan đến trật tự kia. Bạn mã hóa nó vào các điểm X, rồi cho giảm chúng đi sau mỗi lần gọi hàm. Cứ xếp nhầm cái phương tiến thì thơ của bạn sẽ ngược y chang vậy, và đây là cái lỗi sai ngớ ngẩn phổ biến nhất khi port từ ngang lên dọc
Tọa độ định vị: từ trên xuống dưới, phải qua trái
Có hai phương trục tham gia nhưng bị giằng co ngược chiều nhau nên cần định nghĩa cụ thể một chút. HotPDF đo tọa độ từ phía dưới cùng góc tay trái của trang văn bản, với tọa độ Y tăng theo chiều đâm ngược lên, bằng đơn vị point. Do đó, một cột dọc thực chất xuất phát với tọa độ Y cao vọt (nằm kề phía đỉnh của tờ giấy) rồi cứ thế các ký tự bị đè xuống vì HotPDF tự động trừ đi chiều cao của font chữ sau từng âm tự. Bạn cài đặt vị trí Y gốc này duy nhất một lần cho một cột và bộ thư viện lo nốt phần rơi tự do
Riêng cái trục ngang bạn lại phải điều khiển bằng tay. Mỗi cột neo trên một tọa độ X riêng, và mọi cột theo sau lại nhảy nấc giảm đi qua từng cột vì trình tự đọc văn bản gốc chạy từ phải qua trái. Điệu nhịp nghe có vẻ ổn áp nhất là chọn tọa độ X cho cột phải vách ngăn lề, rồi trừ bớt đi khoảng trống hở cho mỗi lần gọi tiếp sau, theo đúng cách ColGap thực hiện trong ví dụ minh hoạ. Bạn được phép tùy chọn kẽ hở đó; nhưng quá kề sẽ bị va hai bên, nhưng thả lỏng lại làm bố cục nhìn rời rạc. Với văn bản nội dung ở kích thước font từ 12-18, một kẽ cột lớn nhỉnh hơn cái kích thước chữ chừng đôi chút đọc khá bắt mắt
Glyph phải thay đổi hình dáng
Một vài âm tự không chỉ đơn giản là bản xoay ngang của bộ gốc; mà chúng còn bắt buộc phải thay thành ký tự khác lúc văn bản lật dọc. Trường hợp duy nhất HotPDF lo liệu dùm bạn là nét sổ âm dài ー (U+30FC) ở tiếng Nhật, thứ vạch trường âm hay gặp ở những từ katakana cỡ như コーヒー. Lúc viết chữ ngang thì nó trông như đoạn gạch nhỏ ngang theo đường nét chuẩn. Nếu chồng khối nó vào trong cột thì nó sẽ lại nằm dẹp ngang xuyên thủng khối chữ, việc đấy là ngớ ngẩn: trong phong cách chữ dọc chuẩn Nhật thì vạch đấy sẽ dựng cột (U+007C) bám nối hai nét của các từ trước và sau nó. HotPDF đánh chặn được U+30FC vào dòng chảy văn bản dọc và render ra đúng thanh nối chữ dọc thế nên cái âm dài lúc nào cũng hiện lên trơn tru mà chả cần bạn phải động móng tay
Cái màn thay ký tự kia chắp gánh trường hợp hay phá banh mấy thuật toán gà con nhất, nhưng bạn cũng cần phải lận lưng chuyện gì nếu cục diện chung này khoét hơi sâu. Viết chữ dọc tử tế đầy đủ tức là nó phải lật ngửa hết những con chữ tiếng Latin cùng cả dấu nối hệ tiếng Tây với một góc chín chục độ, đẩy lệch kana cho vừa chữ rồi bẻ chĩa ngoặc cùng dấu phẩy qua dạng chuẩn cho đọc dọc, với một bộ khung đầy đủ như thế chui tọt vào bên trong cơ chế tính năng viết dọc tại nền OpenType thay cho mấy thuật toán cài code cứng nhắc. Khi một font đã nhúng kèm thì HotPDF thừa sức moi mấy tiện ích đấy ra: hệ dọc xoay chiều (có mã vert cùng vrt2 của nhánh thay GSUB) hay dãn kẽ chữ dọc (mã vkrn hay vpal bên GPOS) vốn là dạng opt-in (cần khai báo tùy chọn kích hoạt), và nó chỉ mọc kẽ chui lọt dọc theo nhánh vẽ dọc lúc font đang active (hoạt động) thực sự chứa bộ khai mã trên. Còn với nội dung thập cẩm pha tạp văn bản CJK như của Aerial Unicode MS đang được sử dụng ở nhiều phạm vi, sự kết hợp đệm xử lý U+30FC mọc từ nền chuẩn cùng mức dãn bước khoảng cách đều đặn kia là thừa đẹp để nặn ra cột nội dung chuẩn xác và đọc sướng mắt rồi; cái mâm OpenType kia chỉ lộ diện bản lĩnh lúc bạn đá qua bộ font nặn chuyên để xắp tinh chỉnh hệ chữ dọc và hăm hở xài độ định vị kana gốc kèm mức chèn kẽ giữa từng bộ gõ
Chế biến script đa mã lệnh kèm góc chữ trên cùng một trang
Rất hiếm khi tồn tại mấy cái trang tài liệu hoàn toàn sạch sẽ. Một trang dọc thuần Nhật thi thoảng rụng vô đoạn ghi danh tiếng Anh ngang, dòng phân phiên mục dưới chân hay hẳn cái mớ hỗn độn chữ tiếng Hàn xen kẽ dọc ngang vào nội dung tiếng Nhật. Nhờ có cấu hình tùy biến viết dọc là cờ nằm trên hàm cài đặt font chữ nên bạn cứ thế hòa nhập đống đấy lại chỉ bằng cách luân chuyển qua lại hàm SetFont để dàn ngang chứ chả cần phải vất vả thao túng trạng thái cồng kềnh cho tổng quan toàn trang tài liệu. Xắp một font chữ viết vắt ngang, đắp mấy cái header cùng số tự nhảy, xong set luôn phông chữ dọc lên, bón cột chữ, xong lại cài chữ vắt ngang tiếp xuống tận footer phần chân bìa. Cứ rớt vô khu vực nào thì bộ cấu trúc bắt lấy SetFont mới nhất, vậy chốt lại cái khuôn khổ kĩ năng quy trình cần nắm duy nhất đó là nổ lệnh gọi bất cứ khi nào bạn rẽ nhánh
Một điểm vụn vặt cần cân nhắc kỹ càng khi băm trộn đống ngôn ngữ: hệ ngữ biểu ý từ tiếng Trung, tiếng Nhật và tiếng Hàn có phần ngả về ô vuông đều khối rồi chúng xếp dọc vừa zin từng bậc với nhau, thế nhưng hệ ngữ Latin lọt thỏm vào trong ruột cột dọc đó sẽ đếch có bộ mã nhảy bước đệm chuẩn hóa như nhau. Nếu khát đến mức chèn hẳn mẩu tiếng Latin ngắn vào cái ruột văn bản đáng lẽ phải là nội dung viết dọc ấy thì, cứ mạnh dạn chọn bẻ ngoặt 90 độ lật dọc đoạn chữ đấy để thả tuột xuống dưới đáy rồi để nó nằm im một cục vắt ngang ngắn hẹp, nhớ cẩn thận lôi hàm tự set lệnh cài bằng riêng TextOut cho phân mảng đó đi chứ đừng xài luôn cái nấc băm đều chuẩn của ngữ chữ Hán nọ. Tách mảng đống tạp hóa ấy như bài toán độc lập lúc sắp bố cục sẽ duy trì độ tĩnh nhịp thở của hệ cột chữ
Từ code chạy demo cho vọt thành bản Production
Nhỏ mà có võ và băm nhỏ thế đâm chắp ghép có thể kiểm soát hoàn toàn. Mở ngỏ lệnh cờ vertical lên ở bộ gọi SetFont, chọt một nút lệnh nổ TextOut cho mỗi cột thẳng đứng tính từ một gốc Y trên đỉnh, xong lùi trừ dần gốc X luân phiên trong đợt phát lệnh cho cột lật nếp đọc từ tay phải móc ngược tay trái. Chịu khó khóa nhúng embedding font kia đi cho đống ngữ CJK thọ tới lúc chui lên cái máy khách, với lại giao luôn cho cục linh kiện gánh trọn vụ bản vá đường thanh chữ U+30FC trường âm nọ và lôi cả mâm tiện ích tính năng vertical có sẵn trên hệ OpenType nếu bộ font của bạn đã hỗ trợ. Vượt rào tới đó thì việc ép layout leo hạng production giờ thuần tuý là ráp giải toán học: móc tọa độ trục ngang cột gốc vách từ cái chiều rộng khối block đã đo định lượng chuẩn chỉnh sẵn, vỡ mảng cái đoạn chữ trường giang đằng đẵng kia bung vô mấy cái cột cắm làm sao lọt thỏm vừa khít độ cao khổ giấy, cộng thêm nhín dư thừa chỗ làm vách kẽ trang trí dán header chân bìa chữ ngang
Nếu hóng cao vọng xa đến phân đoạn tổng quát mảng nội dung hoặc giao diện mặt font nảy số qua mớ mấu chốt được cấu trúc nền phía bên trên kia gồm quy ước viết chữ ngang của hàm TextOut và thao tác ghi danh hệ ngôn ngữ Unicode font chữ, hãy ghé bộ link có liên đới bao gồm ví dụ Hello World hỗ trợ ngôn ngữ toàn cầu và luôn cả Code mẫu nổ text của TextOut. Cần biết thì mảng SetFont bật công tắc cờ dọc với chùm lệnh kích TextOut minh họa tại bài này đều ngậm trọn gói tính năng nằm trực thuộc của bộ Thành phần (Component) HotPDF build xài mượt rành rẽ ở nền tảng Delphi với C++Builder