Τεχνικό Άρθρο

PDFlibPas Συλλαβισμός και Ισορροπημένες Στήλες στο Delphi

Το PDFlibPas ρέει ένα δεσμευμένο κείμενο μέσα από μία έως 64 στήλες ίσου πλάτους με την DrawTextFlowColumns, και σπάει τις γραμμές του με οριοθετημένο συλλαβισμό ευαίσθητο στη γλώσσα μόλις καλέσετε τις SetTextFlowLanguage και SetTextFlowHyphenation. Εννέα γλώσσες υποστηρίζονται, και η γλώσσα μπορεί να κληρονομηθεί από την τιμή /Lang του Catalog του εγγράφου αντί να οριστεί ανά flow

Και τα δύο χαρακτηριστικά υπάρχουν για τον ίδιο λόγο: μια στενή στήλη είναι εκεί όπου ο αφελής χωρισμός γραμμών σταματά να μοιάζει με στοιχειοθεσία και αρχίζει να μοιάζει με αναφορά σφάλματος

Γιατί το justified κείμενο διαλύεται σε στενές στήλες;

Επειδή η justification διανέμει τον περισσευούμενο χώρο στα κενά λέξεων μιας γραμμής, και το ποσό που περισσεύει εξαρτάται από το τι χωράει. Σε ένα ευρύ μέτρο το περίσσευμα είναι μικρό και το μάτι ποτέ δεν το προσέχει. Μισέψτε το πλάτος και μια μοναδική μεγάλη λέξη που δεν χωράει σπρώχνεται στην επόμενη γραμμή, αφήνοντας τις προηγούμενές της να απορροφήσουν όλο εκείνο τον χώρο. Τρεις τέτοιες γραμμές στη σειρά παράγουν τα κάθετα λευκά κανάλια που οι τυπογράφοι ονομάζουν ποτάμια, και οι αναγνώστες τα βιώνουν ως κείμενο δύσκολο να ακολουθήσουν χωρίς να ξέρουν γιατί

Ο συλλαβισμός διορθώνει την αιτία αντί για το σύμπτωμα επιτρέποντας ένα σπάσιμο μέσα στη λέξη. Οι γερμανικές και ολλανδικές σύνθετες λέξεις το κάνουν μη διαπραγματεύσιμο: ένα ουσιαστικό 24 χαρακτήρων σε στήλη 60 χιλιοστών δεν έχει καλή έκβαση χωρίς σημείο σπασίματος. Τα αγγλικά ανέχονται καλύτερα την απουσία, γι' αυτό προϊόντα με αγγλική προτεραιότητα συχνά αποστέλλουν κώδικα διάταξης που καταρρέει την πρώτη φορά που τρέχει ένας γερμανός πελάτης

Ποιες γλώσσες, και από πού προέρχεται η γλώσσα;

Ο συλλαβισμός καλύπτει αγγλικά, γερμανικά, ολλανδικά, γαλλικά, ισπανικά, ιταλικά, πορτογαλικά, ρωσικά και τουρκικά. Ορίστε τον ρητά ανά flow με την SetTextFlowLanguage, ή αφήστε τον να κληρονομηθεί από την καταχώρηση /Lang του Catalog του εγγράφου, την τιμή που ένα tagged και προσβάσιμο έγγραφο ήδη φέρει

Αυτή η κληρονομικότητα αξίζει να χρησιμοποιηθεί αντί να παρακαμφθεί. Ένα έγγραφο που δηλώνει τη γλώσσα του στο Catalog λέει το ίδιο γεγονός σε screen readers, ευρετήρια αναζήτησης και συλλαβισμό από ένα σημείο, και ένα σημείο είναι εκεί όπου πρέπει να ζει ένα γεγονός. Αν ήδη παράγετε tagged έξοδο όπως περιγράφεται στο αυτόματη επισήμανση tag για προσβάσιμα PDF, η καταχώρηση γλώσσας είναι ήδη ορισμένη και το flow μπορεί απλά να την ακολουθήσει

var
  Lib: TPDFlib;
  Flow, Drawn: Integer;
begin
  Lib := TPDFlib.Create;
  try
    Lib.SetOrigin(1);
    Lib.AddTrueTypeFont('Georgia', 1);
    Lib.SetTextSize(10.5);

    Flow := Lib.NewTextFlow(ArticleBody);
    try
      Lib.SetTextFlowLanguage(Flow, 'de');
      // Ενεργό, τουλάχιστον 3 χαρακτήρες πριν το σπάσιμο, 3 μετά
      Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);
      Lib.SetTextFlowMinLines(Flow, 2);   // ποτέ μη αφήνετε μοναχική μία γραμμή

      repeat
        // Τρεις στήλες σε μια περιοχή 480 pt, gutters 18 pt, ισορροπημένες
        Drawn := Lib.DrawTextFlowColumns(Flow, 72, 720, 480, 620, 3, 18, 1);
        if (Drawn = 0) or (Lib.TextFlowFinished(Flow) = 1) then
          Break;
        Lib.NewPage;
      until False;
    finally
      Lib.ReleaseTextFlow(Flow);
    end;

    Lib.SaveToFile('newsletter.pdf');
  finally
    Lib.Free;
  end;
end;

Τα MinPrefix και MinSuffix είναι τυπογραφία, όχι επικύρωση

Οι δύο ακέραιοι μετά τη σημαία ενεργοποίησης ορίζουν τον ελάχιστο αριθμό χαρακτήρων που πρέπει να παραμένουν πριν και μετά ένα σπάσιμο. Τρία και τρία είναι μια συντηρητική προεπιλογή που αποδέχονται τα περισσότερα house styles. Δύο και δύο παράγει περισσότερες ευκαιρίες σπασίματος και αισθητά ασχημότερα αποτελέσματα, επειδή ένα τμήμα δύο γραμμάτων που κρέμεται στο τέλος μιας γραμμής διαβάζεται ως τυπογραφικό λάθος

Ανεβάστε τα ελάχιστα όταν η γραμματοσειρά είναι μεγάλη, όπου κάθε τμήμα είναι οπτικά έντονο, και χαμηλώστε τα μόνο όταν η στήλη είναι γνησίως στενή και έχετε αποφασίσει ότι ένα σφιχτό μέτρο μετράει περισσότερο από ένα καθαρό. Αυτό είναι απόφαση house-style παρά τεχνική, κάτι που είναι ακριβώς ο λόγος που είναι παράμετρος αντί για σταθερά

Τι σημαίνει πράγματι "ισορροπημένο" εδώ;

Η παράμετρος Balance αλλάζει συμπεριφορά μόνο στο τέλος ενός κειμένου. Με ισορροπία ενεργή, οι στήλες κονταίνουν σε ακριβώς ίσο αριθμό γραμμών όταν ό,τι απομένει χωράει μέσα στην περιοχή, κάτι που είναι αυτό που σταματά μια τελευταία σελίδα να δείχνει δύο πλήρεις στήλες και μια τρίτη με μία μοναχική γραμμή. Όταν το κείμενο δεν χωράει, κάθε στήλη κρατά το πλήρες ύψος της ώστε η σελίδα να κουβαλά όσο περισσότερο κείμενο μπορεί και το υπόλοιπο να συνεχίζει στην επόμενη σελίδα

Αυτή η ασυμμετρία είναι η σωστή προεπιλογή για συνεχή έγγραφα. Η ισορροπία στη μέση ενός ρέοντος άρθρου θα σπαταλούσε κατακόρυφο χώρο σε κάθε σελίδα για ένα διακοσμητικό εφέ που κανείς δεν βλέπει, αφού οι στήλες είναι ούτως ή άλλως πλήρεις. Η ισορροπία στο τέλος είναι εκεί όπου το μάτι πράγματι το προσέχει, και ακριβώς εκεί εφαρμόζεται

Ο χωρισμός γραμμών μετρά ολόκληρες λέξεις

Ο αλγόριθμος χωρισμού μετρά πλήρεις λέξεις αντί να συσσωρεύει πλάτη χαρακτήρων, και διατηρεί μια οριοθετημένη αναζήτηση για υπερμεγέθη tokens που δεν χωράνε καθόλου σε μια γραμμή, όπως ένα URL ή έναν αριθμό λογαριασμού. Αυτό κρατά την κοινή περίπτωση γρήγορη και την παθολογική περίπτωση οριοθετημένη, αντί το αντίστροφο

Προαιρετικά soft hyphens και αυτόματα ενωτικά αποδίδονται μόνο όταν το σπάσιμο που σημειώνουν είναι το σπάσιμο που επιλέγεται τελικά. Αυτό ακούγεται προφανές και είναι ένα κλασικό ελάττωμα: μια αφελής υλοποίηση γράφει τον χαρακτήρα ενωτικού κατά τη μέτρηση, και αν το σπάσιμο μετακινηθεί, το ενωτικό μένει πίσω στη μέση μιας λέξης. Τίποτα δεν μοιάζει περισσότερο με σπασμένη μηχανή κειμένου από ένα αδέσποτο ενωτικό μέσα σε μια λέξη

var
  Lib: TPDFlib;
  Flow, Needed: Integer;
begin
  // Αποφασίστε τη διάταξη πριν σχεδιάσετε οτιδήποτε
  Flow := Lib.NewTextFlow(ArticleBody);
  try
    Lib.SetTextFlowLanguage(Flow, 'fr');
    Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);

    // Γραμμές που χρειάζεται το υπόλοιπο κείμενο σε ένα πλάτος στήλης
    Needed := Lib.MeasureTextFlow(Flow, 148);
    if Needed > 3 * LinesPerColumn then
      UseTwoPageSpread
    else
      UseSinglePage;

    Lib.DrawTextFlowColumns(Flow, 72, 720, 480, 620, 3, 18, 1);

    if Lib.TextFlowFinished(Flow) <> 1 then
      CarryOver(Lib.GetTextFlowRemaining(Flow));
  finally
    Lib.ReleaseTextFlow(Flow);
  end;
end;

Κρατήστε τις ρυθμίσεις γραμματοσειράς ίδιες σε όλα τα πλαίσια

Ένας κανόνας διέπει κάθε διάταξη βασισμένη σε flow και αξίζει να ειπωθεί ξεκάθαρα: οι DrawTextFlow, DrawTextFlowColumns και MeasureTextFlow όλες σπάνε γραμμές χρησιμοποιώντας τη γραμματοσειρά που είναι επιλεγμένη τη στιγμή που καλούνται. Αλλάξτε τη γραμματοσειρά ή το μέγεθος ανάμεσα σε δύο πλαίσια του ίδιου flow, ή ξεκινήστε μια νέα σελίδα χωρίς να επανεπιλέξετε μία, και το δεύτερο πλαίσιο σπάει διαφορετικά από ό,τι μέτρησε το πρώτο

Το σύμπτωμα είναι εξοργιστικό ακριβώς επειδή φαίνεται διαλείπον: κείμενο που χωράει στη σελίδα ένα υπερχειλίζει στη σελίδα δύο, ή ένας μετρημένος αριθμός γραμμών διαφωνεί με αυτό που σχεδιάστηκε. Επιλέξτε τη γραμματοσειρά μία φορά πριν τον βρόχο, επανεπιλέξτε την μετά από κάθε NewPage, και το flow συμπεριφέρεται σωστά. Όταν μικτές γραφές εμφανίζονται στο ίδιο κείμενο, η επίλυση που περιγράφεται στο αυτόματο font fallback για CJK και emoji κείμενο εφαρμόζεται τόσο στη μέτρηση όσο και στο σχέδιασμα, ώστε τα πλάτη να παραμένουν συνεπή και στα runs fallback

Για διατάξεις αναφορών όπου το flow είναι ένα στοιχείο ανάμεσα σε κεφαλίδες, υποσέλιδα και μπλοκ βασισμένα σε δεδομένα, τα μοτίβα σύνθεσης στο η μηχανή αναφορών dataset συνδυάζονται καθαρά με στήλες flow: μετρήστε πρώτα, τοποθετήστε το σταθερό υλικό, και μετά δώστε στο flow όποια περιοχή απομένει

Το PDFlibPas είναι μια βιβλιοθήκη PDF για Delphi, C++Builder και Lazarus, και ολόκληρος ο κύκλος ζωής TextFlow, δημιουργία, σχέδιαση, μέτρηση, επιθεώρηση, rewind και απελευθέρωση, εκτίθεται και μέσω των interfaces DLL και ActiveX. Πλήρης τεκμηρίωση βρίσκεται στη σελίδα του PDFlibPas Delphi PDF library