Βάλτε ένα ιαπωνικό μυθιστόρημα στη σελίδα και το πρώτο πράγμα που παρατηρείτε είναι ότι το κείμενο τρέχει προς τα κάτω στη στήλη, όχι κατά μήκος της γραμμής, και ότι οι στήλες προχωρούν από τη δεξιά άκρη του φύλλου προς τα αριστερά. Ένας αναγνώστης που μεγάλωσε με αυτό βρίσκει το οριζόντιο κείμενο κάπως αποστειρωμένο. Το κατασκευαστικό πρόβλημα (engineering problem) είναι ότι το PDF, όπως σχεδόν κάθε ψηφιακό σύστημα κειμένου, κατασκευάστηκε γύρω από μια οριζόντια γραμμή βάσης που αναπτύσσεται από αριστερά προς τα δεξιά, και μια ροή περιεχομένου (content stream) δεν έχει την έννοια της «γραφής αυτής της παραγράφου προς τα κάτω αντί αυτού». Έτσι, όταν μια εφαρμογή Delphi πρέπει να δημιουργήσει ένα πιστοποιητικό, ένα ποίημα, ένα κομμάτι σήμανσης ή ένα νομικό έγγραφο παραδοσιακής μορφής για έναν Ταϊβανέζο, Ιάπωνα ή Κορεάτη αναγνώστη, η διάταξη πρέπει να συναρμολογηθεί με το χέρι: ένας χαρακτήρας κάτω από τον επόμενο, μια στήλη στα αριστερά της προηγούμενης
Η HotPDF σας δίνει έναν διακόπτη που κάνει την καταγραφή (bookkeeping) ανά χαρακτήρα για εσάς. Η γραμματοσειρά που ορίζετε φέρει μια σημαία (flag) IsVertical, και μόλις ενεργοποιηθεί, μια μεμονωμένη κλήση TextOut στοιβάζει μια ολόκληρη συμβολοσειρά σε μια κατακόρυφη στήλη αντί να την τρέχει κατά μήκος μιας γραμμής βάσης. Η τοποθέτηση των στηλών, η σειρά από δεξιά προς τα αριστερά, και μια ήσυχα σημαντική αντικατάσταση γλυφής είναι αυτά που πραγματεύεται το υπόλοιπο αυτής της σελίδας

Ο διακόπτης βρίσκεται στην SetFont
Η κάθετη διάταξη δεν είναι ιδιότητα της σελίδας ή του εγγράφου. Είναι ιδιότητα της γραμματοσειράς με την οποία σχεδιάζετε, και την ενεργοποιείτε με το πέμπτο όρισμα της 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
Επειδή η σημαία ταξιδεύει με τη γραμματοσειρά, εναλλάσσεστε μεταξύ οριζόντιας και κάθετης γραφής απλώς καλώντας ξανά την SetFont με ένα διαφορετικό τελευταίο όρισμα. Μια σελίδα μπορεί να χωρέσει έναν οριζόντιο τίτλο στην κορυφή και κάθετο κείμενο σώματος από κάτω του, και η HotPDF κρατά τις δύο λειτουργίες διαχωρισμένες ανά αντικείμενο γραμματοσειράς και όχι ανά σελίδα. Αυτό είναι που κάνει τις μικτές διατάξεις δυνατές χωρίς κανέναν ειδικό χειρισμό λειτουργίας από την πλευρά σας: κάθε TextOut μετά από μια κάθετη SetFont στοιβάζει, κάθε TextOut μετά από μια οριζόντια τρέχει κατά μήκος της γραμμής βάσης, και η τελευταία SetFont κερδίζει
Το τέταρτο όρισμα είναι το σύνολο χαρακτήρων των Windows, το ίδιο που παίρνει και μια οριζόντια κλήση. Περνώντας το DEFAULT_CHARSET επιτρέπετε στο σύστημα να επιλύσει γλυφές ανά συμβολοσειρά, πράγμα που έχει σημασία εδώ επειδή ένα κάθετο έγγραφο συχνά αναμειγνύει γραφές. Όλα τα υπόλοιπα σχετικά με τη γραμματοσειρά εξακολουθούν να ισχύουν: πρέπει να είναι εγκατεστημένη στο μηχάνημα κατασκευής, και σχεδόν πάντα θέλετε FontEmbedding := True ώστε το αρχείο να αποδίδει τις ίδιες γλυφές CJK σε έναν αναγνώστη (reader) που ποτέ δεν είχε το Arial Unicode MS
Μία κλήση TextOut είναι μία στήλη
Με μια ενεργή κάθετη γραμματοσειρά, μια κλήση TextOut δεν απλώνει πλέον τη συμβολοσειρά της πλαγίως από το δοσμένο σημείο. Φυτεύει τον πρώτο χαρακτήρα στην κορυφή και κατεβάζει τους υπόλοιπους κατευθείαν προς τα κάτω, προχωρώντας κατά το ύψος γραμμής της γραμματοσειράς για κάθε γλυφή. Το X που περνάτε καθορίζει τη στήλη. Το Y που περνάτε καθορίζει από πού ξεκινά η κορυφή της στήλης. Επομένως, για να διατάξετε ένα πραγματικό κείμενο (passage), εκδίδετε μία TextOut ανά στήλη και κάνετε βήμα προς τα αριστερά στο X μεταξύ των κλήσεων, επειδή οι στήλες CJK διαβάζονται από δεξιά προς τα αριστερά
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;
Το ποίημα διαβάζεται όπως πρέπει: η δεξιότερη στήλη πρώτα, από πάνω προς τα κάτω, έπειτα το μάτι πηδά αριστερά στην επόμενη στήλη. Τίποτα στη συμβολοσειρά δεν κωδικοποιεί αυτήν τη σειρά. Εσείς την κωδικοποιείτε στις συντεταγμένες X, μειώνοντάς τες κλήση με κλήση. Εάν κάνετε την κατεύθυνση ανάποδα και ο στίχος βγαίνει ανεστραμμένος, αυτό είναι το πιο συνηθισμένο λάθος κατά τη μεταφορά μιας οριζόντιας διάταξης σε κάθετη
Συντεταγμένες: κάτω στη σελίδα, από δεξιά προς τα αριστερά
Δύο άξονες παίζουν και τραβούν σε αντίθετες κατευθύνσεις, επομένως αξίζει να είμαστε ακριβείς. Η HotPDF μετράει από την κάτω αριστερή γωνία της σελίδας, με το Y να αυξάνεται προς τα πάνω, σε στιγμές (points). Επομένως, μια κάθετη στήλη ξεκινά από ένα υψηλό Y (κοντά στην κορυφή του φύλλου) και οι χαρακτήρες κατεβαίνουν από εκεί, καθώς η HotPDF αφαιρεί ένα ύψος γραμμής για τον καθένα. Εσείς ρυθμίζετε αυτό το αρχικό Y μία φορά ανά στήλη και το component χειρίζεται την κάθοδο
Ο οριζόντιος άξονας είναι αυτός που οδηγείτε χειροκίνητα. Κάθε στήλη κάθεται στο δικό της X, και οι διαδοχικές στήλες κάνουν βήμα σε μικρότερες τιμές X επειδή η σειρά ανάγνωσης είναι από δεξιά προς τα αριστερά. Ένας λογικός ρυθμός είναι να επιλέξετε το X της δεξιότερης στήλης και, στη συνέχεια, να αφαιρέσετε ένα σταθερό κενό στήλης (column gap) για κάθε επόμενη κλήση, όπως κάνει το παράδειγμα με το ColGap. Το κενό είναι δικό σας να το επιλέξετε. Πολύ σφιχτό και οι γειτονικές στήλες αγγίζονται, πολύ χαλαρό και το μπλοκ φαίνεται αραιό. Για κείμενο σώματος (body text) στις 12 έως 18 στιγμές, ένα κενό λίγο μεγαλύτερο από το μέγεθος της γραμματοσειράς διαβάζεται άνετα
Γλυφές που πρέπει να αλλάξουν σχήμα
Λίγοι χαρακτήρες δεν είναι απλώς περιστραμμένα αντίγραφα του οριζόντιου εαυτού τους. Πρέπει να αντικατασταθούν όταν το κείμενο γίνεται κάθετο. Αυτόν που χειρίζεται η HotPDF για εσάς είναι το ιαπωνικό σύμβολο παρατεταμένου ήχου ー (U+30FC), η μπάρα μακρού φωνήεντος που εμφανίζεται σε λέξεις katakana όπως το コーヒー. Σχεδιασμένο οριζόντια είναι μια μικρή παύλα στη γραμμή βάσης. Εάν στοιβάζατε την ίδια γλυφή σε μια στήλη, θα βρισκόταν οριζόντια κατά μήκος της στήλης, πράγμα που είναι λάθος: στα κάθετα ιαπωνικά το σημάδι γίνεται μια κάθετη πινελιά που συνδέει τους δύο χαρακτήρες ανάμεσα στους οποίους κάθεται. Η HotPDF ανιχνεύει το U+30FC στην κάθετη διαδρομή και το αποδίδει ως μια κάθετη μπάρα (U+007C) ώστε το σημάδι μακρού φωνήεντος να δείχνει προς τη σωστή κατεύθυνση χωρίς καμία δουλειά από εσάς
Αυτή η μεμονωμένη αντικατάσταση καλύπτει την περίπτωση που χαλάει τις περισσότερες αφελείς υλοποιήσεις, αλλά αξίζει να γνωρίζετε πού το γενικό πρόβλημα πηγαίνει βαθύτερα. Η πλήρης κάθετη τυπογραφία (vertical typography) επίσης περιστρέφει λατινικά γράμματα και δυτικά σημεία στίξης ενενήντα μοίρες, μετατοπίζει τα μικρά κάνα (small kana), και επανατοποθετεί παρενθέσεις και κόμματα στις κάθετες μορφές τους, και μια πλήρης υλοποίηση αυτού ζει στα κάθετα χαρακτηριστικά OpenType μιας γραμματοσειράς παρά σε σταθερούς κανόνες. Η HotPDF μπορεί να αξιοποιήσει αυτά τα χαρακτηριστικά όταν η γραμματοσειρά τα φέρει: οι κάθετες εναλλαγές (τα χαρακτηριστικά vert και vrt2 GSUB) και το κάθετο kerning (οι αναζητήσεις GPOS vkrn και vpal) είναι προαιρετικά (opt-in), εφαρμοζόμενα κατά μήκος της κάθετης διαδρομής μόνο όταν η ενεργή γραμματοσειρά τα ορίζει πραγματικά. Για μικτό κείμενο CJK σε μία μόνο γραμματοσειρά ευρείας κάλυψης όπως το Arial Unicode MS, ο ενσωματωμένος χειρισμός του U+30FC συν ένα ομοιόμορφο βήμα ύψους γραμμής αρκεί για την παραγωγή σωστών, ευανάγνωστων στηλών. Τα χαρακτηριστικά OpenType έχουν σημασία όταν προχωράτε σε μια γραμματοσειρά σχεδιασμένη για λεπτή κάθετη ρύθμιση και θέλετε την εγγενή τοποθέτηση κάνα (kana positioning) και το διάστιχο μεταξύ των γλυφών (inter-glyph spacing)
Ανάμειξη γραφών και προσανατολισμών σε μία σελίδα
Τα πραγματικά έγγραφα σπάνια είναι καθαρά. Μια κάθετη ιαπωνική σελίδα μπορεί να φέρει μια οριζόντια αγγλική λεζάντα, έναν αριθμό σελίδας στο κάτω μέρος, ή ένα μπλοκ κορεατικών στημένο κάθετα δίπλα στα ιαπωνικά. Επειδή η κάθετη σημαία (vertical flag) είναι ένας διακόπτης επιπέδου γραμματοσειράς, τα συνθέτετε εναλλάσσοντας κλήσεις SetFont αντί να διαχειρίζεστε οποιαδήποτε κατάσταση επιπέδου σελίδας. Ορίστε μια οριζόντια γραμματοσειρά, γράψτε την τρέχουσα κεφαλίδα (running header) και τον αριθμό σελίδας (folio), ορίστε μια κάθετη γραμματοσειρά, διατάξτε τις στήλες, ορίστε ξανά μια οριζόντια γραμματοσειρά για ένα υποσέλιδο. Κάθε περιοχή παίρνει τη λειτουργία της πιο πρόσφατης SetFont, οπότε η μόνη πειθαρχία που απαιτείται είναι να την καλείτε όποτε αλλάζετε κατεύθυνση
Μια λεπτομέρεια που πρέπει να σχεδιάσετε όταν οι γραφές αναμειγνύονται: Τα κινεζικά, ιαπωνικά και κορεατικά ιδεογράμματα είναι κοντά στο τετράγωνο και στοιβάζονται σε ομοιόμορφο βήμα, αλλά τα λατινικά τμήματα (Latin runs) ενσωματωμένα σε μια κάθετη στήλη δεν έχουν αυτήν την ομοιόμορφη προώθηση. Αν χρειάζεστε λίγες λατινικές λέξεις μέσα σε κατά τα άλλα κάθετο κείμενο, αποφασίστε σκόπιμα εάν θα πρέπει να περιστραφούν για να τρέχουν προς τα κάτω στη στήλη ή να τοποθετηθούν όρθια ως μια μικρή οριζόντια ένθεση (inset), και τοποθετήστε αυτό το τμήμα (fragment) με τη δική του TextOut αντί να το αφήσετε να καβαλήσει το κάθετο βήμα που προορίζεται για ιδεογράμματα. Η αντιμετώπιση του μικτού τμήματος ως του δικού του προβλήματος τοποθέτησης διατηρεί τον ρυθμό της στήλης άθικτο
Από το δείγμα στην παραγωγή
Τα κομμάτια είναι μικρά και συνθέτονται προβλέψιμα. Ενεργοποιήστε την κάθετη σημαία στην SetFont, εκδώστε μία TextOut ανά στήλη από ένα σταθερό Y κορυφής, και μειώστε το X σε όλες τις κλήσεις ώστε οι στήλες να διαβάζονται από δεξιά προς τα αριστερά. Ενσωματώστε τη γραμματοσειρά έτσι ώστε οι γλυφές CJK να επιβιώσουν από το ταξίδι στο μηχάνημα ενός αναγνώστη, και αφήστε το component να χειριστεί το σημάδι μακρού φωνήεντος U+30FC και, όπου τα υποστηρίζει η γραμματοσειρά, τα κάθετα χαρακτηριστικά OpenType. Από εκεί και πέρα, η παραγωγική εφαρμογή (productionizing) της διάταξης είναι κυρίως αριθμητική: εξαγωγή θέσεων X στηλών από ένα μετρημένο πλάτος μπλοκ, διάσπαση μεγάλων κειμένων (passages) σε στήλες που χωρούν στο ύψος της σελίδας, και κράτηση χώρου για οριζόντιες κεφαλίδες (headers) και αριθμούς σελίδας (folios)
Για την ευρύτερη επιφάνεια κειμένου και γραμματοσειράς πάνω στην οποία χτίζεται αυτό, συμπεριλαμβανομένων των οριζόντιων συμβάσεων TextOut και της καταχώρισης γραμματοσειράς Unicode, δείτε το πολύγλωσσο παράδειγμα Hello World και το παράδειγμα TextOut. Ο κάθετος διακόπτης SetFont και οι κλήσεις TextOut που παρουσιάζονται εδώ αποτελούν μέρος του HotPDF Component για Delphi και C++Builder