Ένας αναγνώστης με χαμηλή όραση δεν μπορεί να διακρίνει μαύρο κείμενο σε λευκή σελίδα με την προεπιλεγμένη αντίθεση, οπότε ζητά σκοτεινή λειτουργία. Η αφελής απάντηση είναι η αντιστροφή κάθε pixel της αποδοσμένης σελίδας. Κυκλοφορεί σε μία εβδομάδα και σπάει την επόμενη: σαρωμένες φωτογραφίες επιστρέφουν σαν αρνητικά φιλμ, τα κίτρινα σημάδια highlighter του αναγνώστη μετατρέπονται σε δυσανάγνωστο μπλε κηλίδωμα, και κάποιος ρωτά γιατί η εκτύπωση βγήκε ολόμαυρη. Το χαρακτηριστικό αξίζει πραγματικά να χτιστεί και είναι πραγματικά εύκολο να γίνει μισό-σωστά, και το χάσμα ανάμεσα στα δύο αποτελέσματα είναι μία ιδέα: κάθε απόφαση χρώματος ανήκει σε συγκεκριμένο σημείο του pipeline απόδοσης, και η αντιστροφή είναι το λάθος εργαλείο εφαρμοσμένο στο λάθος στάδιο. Ο κώδικας εδώ χρησιμοποιεί το PDFium Component, τον viewer βασισμένο σε PDFium για Delphi, C++Builder και Lazarus, του οποίου το API απόδοσης εκθέτει αυτά τα στάδια ξεχωριστά
Τα φίλτρα είναι κατάσταση παρουσίασης, ποτέ κατάσταση εγγράφου
Ένας κανόνας αποτρέπει τη χειρότερη κατηγορία σφάλματος εδώ: μια λειτουργία ανάγνωσης αλλάζει τον τρόπο παραγωγής ή μετεπεξεργασίας του bitmap, και τίποτα άλλο. Τα bytes του PDF παραμένουν ανέγγιχτα, κάθε λειτουργία είναι αναστρέψιμη με επανα-απόδοση, και η "αποθήκευση" δεν γράφει ποτέ μια φιλτραρισμένη εμφάνιση πίσω στο αρχείο. Αυτό ακούγεται προφανές μέχρι να εκτυπώσει ένας νομικός αναθεωρητής ένα συμβόλαιο υπό ενεργό φίλτρο και να καταθέσει την αντεστραμμένη έκδοση. Σε εκείνο το σημείο, η ερώτηση "η εκτύπωση χρησιμοποιεί την ίδια εμφάνιση του εγγράφου ή αυτήν της οθόνης" αποδεικνύεται ότι αξίζει μια ρητή απάντηση στο spec σας, όχι ένα ατύχημα διαδρομής κώδικα. Κρατήστε τη ρύθμιση φίλτρου στην κατάσταση του viewer, εφαρμόστε την κατά την απόδοση, και κάντε κάθε διαδρομή εξαγωγής να δηλώνει ποια εμφάνιση χρησιμοποιεί
Ο κανόνας ξεπληρώνεται δύο φορές. Η αναστρεψιμότητα έρχεται δωρεάν, επειδή η εναλλαγή λειτουργιών επανα-αποδίδει από την αμετάβλητη πηγή: δεν υπάρχει στοίβα undo προς συντήρηση και κανένας τρόπος να υποβαθμιστεί η σελίδα από μια σειρά αλλαγών λειτουργίας. Σενάρια πολλαπλών παραθύρων παραμένουν συνεπή για τον ίδιο λόγο. Δύο προβολές του ίδιου εγγράφου μπορούν να τρέχουν διαφορετικές λειτουργίες, αφού κάθε προβολή κατέχει τη δική της κατάσταση παρουσίασης ενώ το αντικείμενο εγγράφου παραμένει κοινόχρηστο
Πρώτα η απόδοση, μετά ο μετασχηματισμός
Το υποστηριζόμενο πρότυπο είναι η μετεπεξεργασία bitmap μετά την απόδοση: το RenderPage παράγει το raster της σελίδας, και μετά ένα πέρασμα μετασχηματισμού το προσαρμόζει. Το component διανέμει τρεις μετασχηματισμούς ως πράξεις bitmap επί τόπου, τα InvertPdfBitmap, DuotonePdfBitmap και GrayscalePdfBitmap, κάτι που κάνει την εναλλαγή λειτουργίας μια καθαρή συνάρτηση δύο σταδίων:
function TViewerForm.RenderWithMode(W, H: Integer): TBitmap;
begin
Result := Pdf.RenderPage(0, 0, W, H, ro0, [reAnnotations]);
case FReadingMode of
rmInverted: InvertPdfBitmap(Result);
rmHighContrast: DuotonePdfBitmap(Result, clBlack, $0000C8FF); // σκούρο φόντο, κεχριμπαρί κείμενο
rmGrayscale: GrayscalePdfBitmap(Result);
end;
// rmNormal περνά χωρίς αλλαγή: το έγγραφο κρατά τα δικά του χρώματα
end;
Δύο πράγματα προκύπτουν από αυτόν τον σχεδιασμό. Πρώτον, το κόστος μετασχηματισμού είναι ανάλογο του μεγέθους bitmap, οπότε η εργασία ανήκει όπου γίνεται caching στα αποτελέσματα απόδοσής σας: φιλτράρετε το cached bitmap μία φορά, όχι σε κάθε ζωγράφισμα. Δεύτερον, επειδή ο μετασχηματισμός τρέχει στο τελικό raster, επηρεάζει κείμενο, διανυσματική τέχνη, εικόνες και εμφανίσεις σχολιασμών με τον ίδιο τρόπο. Αυτή η ομοιομορφία είναι ακριβώς αυτό που η απλή αντιστροφή κάνει λάθος για φωτογραφίες. Είναι ο λόγος που ο μετασχηματισμός duotone κάνει καλύτερη προεπιλογή για έγγραφα με πολύ κείμενο, αφού αντιστοιχίζει τη φωτεινότητα σε μια επιλεγμένη κλίμακα χρώματος από σκούρο προς ανοιχτό αντί να αναστρέφει αποχρώσεις· η αντιστροφή παραμένει διαθέσιμη ως ρητή επιλογή για αναγνώστες που τη θέλουν. Πιο ευκρινείς άκρες γλυφών είναι ένας ξεχωριστός μοχλός. Η επιλογή απόδοσης reNoSmoothText απενεργοποιεί το anti-aliasing κειμένου κατά την απόδοση και ταιριάζει καλά με τη λειτουργία υψηλής αντίθεσης σε μεγάλο zoom
Δύο grayscale που διαφωνούν
Οι επιλογές απόδοσης περιλαμβάνουν το reGrayscale, που μοιάζει με συντόμευση πέρα από το βήμα μετεπεξεργασίας. Δεν είναι η ίδια πράξη:
// Επίπεδο μηχανής: grayscale εφαρμόζεται κατά τη ραστεροποίηση
GrayA := Pdf.RenderPage(0, 0, W, H, ro0, [reGrayscale]);
// Μετεπεξεργασία: απόδοση σε χρώμα, μετατροπή του τελικού bitmap
GrayB := Pdf.RenderPage(0, 0, W, H);
GrayscalePdfBitmap(GrayB);
Η επιλογή σε επίπεδο μηχανής εφαρμόζεται στην raster έξοδο περιεχομένου εικόνας αλλά δεν φτάνει σε διανυσματικά γεμίσματα ή χρώματα κειμένου, οπότε μια σελίδα με έγχρωμους τίτλους μπορεί να επιστρέψει με γκρι φωτογραφίες και επίμονα μπλε τίτλους. Το GrayscalePdfBitmap στο τελικό bitmap μετατρέπει τα πάντα, ανεξαιρέτως. Η επιλογή απόδοσης εξακολουθεί να κερδίζει τη θέση της όταν θέλετε τις εικόνες αποκορεσμένες ενώ διατηρείτε το χρώμα κειμένου ως σήμα, κάτι που ορισμένοι αναγνώστες με χαμηλή όραση προτιμούν συγκεκριμένα. Αλλά αν η απαίτηση γράφει "grayscale σελίδα", η μετεπεξεργασία είναι η έκδοση που την ικανοποιεί. Όποια διαδρομή κι αν επιλέξετε, κρατήστε στο μυαλό σας και τα δύο στυλ υπερφόρτωσης του RenderPage. Η μορφή συνάρτησης επιστρέφει ένα bitmap που ο καλών κατέχει και πρέπει να ελευθερώσει, και αυτό έχει σημασία μόλις τα φίλτρα πολλαπλασιάσουν τον αριθμό των αποδοσμένων bitmap σε εξέλιξη
Φόντα, σημάδια επιλογής, και η παγίδα PageColor
Δεν είναι κάθε προσαρμογή άνεσης ένας μετασχηματισμός. Η αντικατάσταση του λευκού φόντου σελίδας με έναν θερμό τόνο συχνά αρκεί από μόνη της για αναγνώστες ευαίσθητους στη λάμψη, και έχει μια αφιερωμένη ιδιότητα. Η ιδιότητα φέρει έναν κανόνα εμβέλειας που πιάνει κόσμο:
// Επηρεάζει μόνο την προβολή στην οθόνη
PdfView.PageColor := $00D9EDF2; // θερμός τόνος χαρτιού πίσω από το περιεχόμενο σελίδας
// Η έξοδος του RenderPage αγνοεί το PageColor· περάστε το χρώμα ρητά
Bmp := Pdf.RenderPage(0, 0, W, H, ro0, [], $00D9EDF2);
Το PageColor αλλάζει τι εμφανίζει το TPdfView, αλλά τα bitmap που παράγονται μέσω του RenderPage κρατούν το προεπιλεγμένο λευκό εκτός αν η παράμετρος Color ορίζει διαφορετικά. Το σύμπτωμα είναι αξιόπιστο: η οθόνη δείχνει τη χρωματισμένη σελίδα, ο χρήστης εξάγει ή εκτυπώνει, και η έξοδος επιστρέφει στο λευκό. Καταχωρίστε το κάτω από την ίδια απόφαση πολιτικής εξαγωγής από την πρώτη ενότητα
Οι υπόλοιπες ιδιότητες χρώματος ορίζουν σημάδια επικάλυψης: το HighlightColor για ευρήματα αναζήτησης, το SelectionColor για επιλογή κειμένου από τον χρήστη, το ReadingWordColor για τον δρομέα εκφωνούμενης λέξης. Καθένα από αυτά πρέπει να επανελεγχθεί κάτω από κάθε φίλτρο που προσφέρετε. Ένας κεχριμπαρένιος δρομέας ανάγνωσης που δουλεύει σε λευκό εξαφανίζεται μετά την αντιστροφή· μια χλωμή μπλε επιλογή χάνεται μέσα σε ένα φόντο υψηλής αντίθεσης. Διατηρήστε παλέτες επικάλυψης ανά λειτουργία αντί για ένα καθολικό σύνολο, και δοκιμάστε τους συνδυασμούς σκόπιμα. Τα φίλτρα μαζί με τη μετατροπή κειμένου σε ομιλία είναι μια φυσιολογική διαμόρφωση για τους αναγνώστες που εξυπηρετεί αυτό το χαρακτηριστικό, όχι μια οριακή περίπτωση. Ο ίδιος ο μηχανισμός επικάλυψης καλύπτεται στο άρθρο για τον προσβάσιμο reader
Αριθμοί, επαλήθευση, και το ερώτημα της εκτύπωσης
Το WCAG 2.1 μετατρέπει αυτό το χαρακτηριστικό σε κάτι μετρήσιμο. Το κριτήριο επιτυχίας 1.4.3 ζητά λόγο αντίθεσης 4,5:1 για το κύριο κείμενο, και το 1.4.6 τον ανεβάζει σε 7:1 για ενισχυμένη αντίθεση. Δειγματοληπτικά ελέγξτε τη λειτουργία υψηλής αντίθεσης έναντι αυτών των λόγων με έναν αναλυτή αντίθεσης που τρέχει πάνω σε πραγματική αποδοσμένη έξοδο. Κείμενο πάνω σε εικόνες και κείμενο σε πεδία φόρμας είναι τα σημεία όπου οι λόγοι αποτυγχάνουν σιωπηλά ακόμη και όταν το κύριο κείμενο περνά
Η εκτύπωση αξίζει τη δική της απόφαση, και η υπερασπίσιμη προεπιλογή είναι η ίδια εμφάνιση του εγγράφου, με το "εκτύπωση όπως εμφανίζεται" προσφερόμενο ως ρητή επιλογή χρήστη. Μια εκτυπωμένη σελίδα είναι αποδεικτικό στοιχείο σε περισσότερες ροές εργασίας από όσο τείνουν να περιμένουν οι δημιουργοί viewer, και μια αντεστραμμένη εκτύπωση ενός συμβολαίου είναι ένα περιστατικό υποστήριξης με νομική χροιά. Ένας ακόμη συνδυασμός έχει σημασία για την απόδοση: η φιλτραρισμένη απόδοση διπλασιάζει τη δουλειά bitmap σε κάθε εναλλαγή λειτουργίας, οπότε μην εφαρμόζετε μετασχηματισμό σε κάθε μήνυμα ζωγραφίσματος. Κάντε cache το φιλτραρισμένο bitmap και ξανατρέξτε τον μετασχηματισμό μόνο όταν πράγματι αλλάζει η σελίδα, το zoom ή η λειτουργία. Η στρατηγική caching που το κάνει αυτό φθηνό βρίσκεται στο άρθρο για την cache απόδοσης και την απόδοση zoom
Ένα πράγμα να αποφασίσετε στο UI σας παρά στον κώδικά σας: ποια λειτουργία είναι η σωστή προεπιλογή. Δεν υπάρχει μία μοναδική απάντηση, οπότε προσφέρετε το σύνολο και αφήστε τον αναγνώστη να διαλέξει. Η υψηλή αντίθεση ταιριάζει στην πλειονότητα της ανάγνωσης με πολύ κείμενο, η αντιστροφή ταιριάζει σε αναγνώστες που θέλουν συγκεκριμένα φως-σε-σκούρο, το grayscale κόβει τον θόρυβο χρώματος, και μια απόχρωση φόντου διαχειρίζεται την ευαισθησία στη λάμψη. Διατηρήστε την επιλογή ανά χρήστη, επαναφέρετέ την κατά την εκκίνηση, και κρατήστε μια διαδρομή ενός πατήματος πλήκτρου πίσω στο κανονικό, αφού ένας αναγνώστης που καταλήγει σε μια λειτουργία που δεν μπορεί να διαβάσει χρειάζεται γρήγορη διέξοδο
Οι επιλογές απόδοσης, οι μετασχηματισμοί bitmap και οι ιδιότητες χρώματος προβολής που χρησιμοποιούνται εδώ διανέμονται με το PDFium Component για Delphi, C++Builder και Lazarus/FPC, με πλήρη πηγαίο κώδικα ώστε οι υλοποιήσεις μετασχηματισμού να μπορούν να ελεγχθούν ή να επεκταθούν