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

HotXLS: Προστασία φύλλου, διαμόρφωση σελίδας και εκτύπωση στο Delphi

Τρεις ομάδες ρυθμίσεων φύλλου εργασίας δεν έχουν καμία σχέση με τις τιμές των κελιών, αλλά σχετίζονται εξ ολοκλήρου με τον τρόπο συμπεριφοράς του αρχείου μόλις αυτό φύγει από τον κώδικά σας. Η προστασία φύλλου καθορίζει ποια κελιά μπορεί να επεξεργαστεί ένας χρήστης αφού παραδώσετε το βιβλίο εργασίας. Η διαμόρφωση σελίδας καθορίζει τον προσανατολισμό, το μέγεθος χαρτιού και τα περιθώρια. Οι ρυθμίσεις εκτύπωσης (επαναλαμβανόμενες γραμμές τίτλου, κλιμάκωση και χειροκίνητα διαλείμματα σελίδας) ελέγχουν τον τρόπο με τον οποίο ένα πλέγμα αυθαίρετου μήκους καταλήγει στο χαρτί. Καμία από τις τρεις δεν φαίνεται όταν απλώς κοιτάτε τα δεδομένα σε έναν viewer, και όλες τους χαλάνε αθόρυβα στην πράξη όταν είναι λανθασμένες. Το HotXLS, μια εγγενής βιβλιοθήκη υπολογιστικών φύλλων για Delphi και C++Builder, εκθέτει την πλήρη επιφάνεια για .xls και .xlsx, πράγμα που σημαίνει ότι αναπαράγει επίσης κάθε αντι-διαισθητικό κανόνα του Excel ενσωματωμένο σε αυτήν την επιφάνεια

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

Κάθε κελί γεννιέται κλειδωμένο

Το πρότυπο ECMA-376 ορίζει το locked ως τμήμα της εγγραφής μορφοποίησης ενός κελιού, όχι ως ιδιότητα της ίδιας της προστασίας, και η προεπιλεγμένη τιμή του είναι true. Η προστασία φύλλου είναι απλώς ο διακόπτης που καθιστά τη σημαία εφαρμόσιμη. Έτσι, ολόκληρο το πλέγμα φέρει μια σημαία κλειδώματος από τη στιγμή που δημιουργείται, σε λανθάνουσα κατάσταση, και η κλήση της Protect τις ενεργοποιεί όλες ταυτόχρονα. Η λύση είναι να ορίσετε σκόπιμα τη σειρά: χτίστε τη διάταξη, ξεκλειδώστε ρητά τα εύρη που πρέπει να επεξεργάζονται οι χρήστες, και προστατέψτε τελευταία

Διάγραμμα της σειράς προστασίας HotXLS σε Delphi όπου κάθε κελί γεννιέται με locked true, τα εύρη εισόδου ξεκλειδώνονται πρώτα με SetLocked, και το Sheet.Protect που καλείται τελευταίο κρατά τα ξεκλείδωτα κελιά επεξεργάσιμα
Τα κελιά έρχονται κλειδωμένα από προεπιλογή, οπότε ξεκλειδώστε πρώτα τα εύρη εισόδου και καλέστε την Protect τελευταία για να τα κρατήσετε επεξεργάσιμα
Book := TXLSXWorkbook.Create;
try
  Sheet := Book.Sheets.Add('Timesheet');
  // ... εδώ γράφονται η γραμμή κεφαλίδας, η στήλη ονομάτων και οι τύποι αμοιβής ...
  Sheet.Range['B2:B50'].SetLocked(False);         // εδώ το προσωπικό καταχωρεί ώρες
  Sheet.Range['F2:F50'].SetFormulaHidden(True);   // κρατά ιδιωτικούς τους υπολογισμούς αμοιβής
  Sheet.Protect('review-2026');                   // τώρα ενεργοποιούνται οι σημαίες κλειδώματος
  Book.SaveAs('timesheet.xlsx');
finally
  Book.Free;
end;

Η SetFormulaHidden κάνει κάτι διαφορετικό και εύκολο να παραβλεφθεί: ενώ η προστασία είναι ενεργή, το κελί εξακολουθεί να εμφανίζει την υπολογισμένη τιμή του, αλλά η γραμμή τύπων δεν εμφανίζει τίποτα. Αυτό έχει σημασία όταν ένας τύπος ενσωματώνει τιμές χρέωσης, περιθώρια κέρδους ή συντελεστές βαθμολόγησης που δεν θα θέλατε να δώσετε σε κάθε παραλήπτη που κάνει κλικ σε ένα σύνολο. Στην πρόσοψη XLS η ίδια πρόθεση εκφράζεται ανά εύρος μέσω των IXLSRange.Locked και FormulaHidden. Το φύλλο εργασίας εκεί φέρει επίσης δεκαπέντε σημαίες Allow* (AllowSort, AllowAutoFilter, AllowFormatCells, και οι υπόλοιπες), οπότε ένα προστατευμένο φύλλο μπορεί ακόμα να ταξινομηθεί και να φιλτραριστεί αντί να παγώσει σε ένα σφραγισμένο έκθεμα

Τι προστατεύει πραγματικά ο κωδικός πρόσβασης προστασίας

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

Ένα επίπεδο πιο πάνω, η ProtectWorkbook στην πρόσοψη XLSX κλειδώνει τη δομή του βιβλίου εργασίας, κάτι που εμποδίζει την προσθήκη, τη μετονομασία, τη διαγραφή ή την αναδιάταξη φύλλων. Ορίστε τη όποτε η λίστα φύλλων αποτελεί η ίδια ένα συμβόλαιο με έναν parser που έπεται και ευρετηριάζει τα φύλλα με βάση το όνομα ή τη θέση τους. Ένα μετονομασμένο φύλλο διακόπτει την εισαγωγή στην άλλη πλευρά εξίσου σίγουρα όσο μια διαγραμμένη στήλη. Η πρόσοψη XLS αντικατοπτρίζει αυτή τη διαστρωμάτωση με την TXLSWorkbook.Protect σε επίπεδο βιβλίου εργασίας και κλήσεις Protect ανά φύλλο, καθώς και μια ιδιότητα isProtected για κώδικα που χρειάζεται να επιθεωρήσει ένα κληρονομημένο αρχείο πριν το τροποποιήσει

Όταν η απαίτηση είναι πραγματική εμπιστευτικότητα, ο μηχανισμός αλλάζει εντελώς. Η SaveAsEncrypted παράγει ένα πακέτο κρυπτογραφημένο με AES σύμφωνα με το σχήμα Standard Encryption του ECMA-376, το οποίο καλύπτεται αναλυτικά στον οδηγό εξόδου XLSX με προστασία AES, ενώ η παλαιού τύπου πρόσοψη XLS γράφει και διαβάζει αρχεία .xls κρυπτογραφημένα με RC4 μέσω της EncryptionPassword και της υπερφόρτωσης με κωδικό πρόσβασης της Open. Η διαφορά δεν είναι ακαδημαϊκή. Ένα προστατευμένο φύλλο ταξιδεύει σε καθαρό κείμενο, οπότε οποιοδήποτε εργαλείο zip μπορεί να διαβάσει τις τιμές των κελιών του, ενώ ένα κρυπτογραφημένο πακέτο είναι μη αναγνώσιμο χωρίς τον κωδικό πρόσβασης. Μια γραμμή ελέγχου που λέει «το αρχείο μισθοδοσίας πρέπει να είναι προστατευμένο» σχεδόν πάντα σημαίνει κρυπτογράφηση, όποιο λεξιλόγιο και αν χρησιμοποιεί

Διάγραμμα που αντιπαραθέτει την προστασία φύλλου HotXLS, που αποθηκεύει παλιό hash 16 bit και αφήνει τιμές κελιών σε cleartext αναγνώσιμο από οποιοδήποτε εργαλείο zip, με έξοδο SaveAsEncrypted AES που μένει δυσανάγνωστη χωρίς τον κωδικό
Η προστασία φύλλου είναι ζώνη ασφαλείας κατά τυχαίων επεξεργασιών ενώ οι τιμές καθαρού κειμένου παραμένουν αναγνώσιμες, και μόνο η κρυπτογράφηση AES κρύβει τα περιεχόμενα

Η διαμόρφωση σελίδας είναι μέρος του συμβολαίου του εγγράφου

Η συμπεριφορά εκτύπωσης είναι αόρατη στην οθόνη, γι' αυτό και τόσο συχνά κυκλοφορεί χαλασμένη. Τη στιγμή που ένας πελάτης εκτυπώνει το βιβλίο εργασίας, ή το εξάγει σε PDF για έναν ελεγκτή, τα περιθώρια, η κλιμάκωση και οι επαναλαμβανόμενοι τίτλοι μετατρέπονται σε λειτουργικές απαιτήσεις που κανείς δεν έχει δοκιμάσει. Στην πρόσοψη XLSX αυτές οι ρυθμίσεις εξαρτώνται απευθείας από το φύλλο εργασίας:

Sheet.PageLandscape := True;
Sheet.PaperSize := xlsxPaperA4;
Sheet.SetPageMargins(0.5, 0.5, 0.75, 0.75, 0.3, 0.3);
Sheet.CenterHeader := 'Monthly Timesheet';
Sheet.RightFooter := 'Page &P of &N';
Sheet.PrintArea := '$A$1:$F$60';     // απλή αναφορά: χωρίς όνομα φύλλου εδώ
Sheet.PrintTitleRows := '$1:$1';     // η γραμμή κεφαλίδας επαναλαμβάνεται σε κάθε σελίδα
Sheet.FitToWidth := 1;
Sheet.FitToHeight := 0;              // επεκτείνεται προς τα κάτω όσο αυξάνονται τα δεδομένα
Sheet.PrintGridlines := False;

Δύο από αυτές τις γραμμές κρύβουν παγίδες. Οι συμβολοσειρές κεφαλίδας και υποσέλιδου χρησιμοποιούν τους κωδικούς μορφοποίησης του Excel: &P για την τρέχουσα σελίδα, &N για το συνολικό πλήθος, με τα &L, &C και &R για να απευθυνθείτε ρητά στις τρεις ενότητες. Η άλλη παγίδα είναι η PrintArea, η οποία σκόπιμα δέχεται μια απλή αναφορά κελιού. Το HotXLS την αποθηκεύει χωρίς προσδιορισμό και προσθέτει το όνομα του φύλλου ως πρόθεμα όταν γράφει το αρχείο, οπότε αν περάσετε εσείς οι ίδιοι το 'Timesheet!$A$1:$F$60' προκύπτει μια διπλά προσδιορισμένη, κακοσχηματισμένη αναφορά. Η ίδια προσοχή ισχύει ένα επίπεδο πιο κάτω: οι περιοχές εκτύπωσης και οι τίτλοι εκτύπωσης διατηρούνται ως τα ενσωματωμένα defined names _xlnm.Print_Area και _xlnm.Print_Titles, οπότε ποτέ μην προσθέτετε καταχωρίσεις _xlnm.* μέσω του DefinedNames με το χέρι, διαφορετικά οι δύο μηχανισμοί θα συγκρουστούν για την ίδια θέση

Κλιμάκωση που αντέχει σε όγκους δεδομένων παραγωγής

Ο συνδυασμός FitToWidth := 1 με FitToHeight := 0 διαβάζεται ως «προσάρμοσε πάντα τις στήλες σε μία σελίδα, και μετά πάρε όσες σελίδες χρειάζονται τα δεδομένα προς τα κάτω», και αποτελεί τη σωστή προεπιλογή για κάθε αναφορά της οποίας ο αριθμός γραμμών μεταβάλλεται. Η παγίδα είναι να ρυθμίσετε ένα σταθερό ποσοστό ή ένα ζευγάρι fit-to-page με βάση ένα δοκιμαστικό αρχείο τριάντα γραμμών: δώστε τις ίδιες ρυθμίσεις σε εξακόσιες γραμμές παραγωγής και η έξοδος είτε εκρήγνυται σε δεκάδες κομμένες σελίδες είτε συρρικνώνεται κάτω από το όριο αναγνωσιμότητας. Κλιμακώστε το πλάτος, αφήστε το μήκος να αυξάνεται, και επαναλάβετε τη γραμμή κεφαλίδας μέσω της PrintTitleRows, ώστε η δέκατη έβδομη σελίδα να παραμένει αναγνώσιμη από μόνη της

Διάγραμμα κλιμάκωσης εκτύπωσης HotXLS σε Delphi με FitToWidth ορισμένο σε 1 ώστε κάθε σελίδα μένει ένα φύλλο φαρδιά, FitToHeight ορισμένο σε 0 ώστε οι σελίδες μεγαλώνουν προς τα κάτω, PrintTitleRows που επαναλαμβάνει τη ζώνη κεφαλίδας, και αλλαγές σελίδας αναγεννημένες μετά το ClearAllPageBreaks
FitToWidth 1 με FitToHeight 0 κρατά κάθε σελίδα ένα φύλλο φαρδιά ενώ επαναλαμβανόμενες γραμμές τίτλου και αναγεννημένα σπάσματα διατηρούν αναγνωσιμότητα

Τα χειροκίνητα διαλείμματα ακολουθούν την ίδια πειθαρχία αναδημιουργίας όπως όλα τα υπόλοιπα σε ένα βιβλίο εργασίας που παράγεται μέσω κώδικα. Η AddRowBreak(BeforeRow) ξεκινά μια νέα σελίδα πριν από ένα όριο ενότητας, αλλά όταν ο generator εκτελείται ξανά και οι γραμμές μετατοπίζονται, ένα παλιό διάλειμμα καταλήγει στη μέση ενός πίνακα. Καλέστε πρώτα την ClearAllPageBreaks, και μετά προσθέστε ξανά διαλείμματα υπολογισμένα από τους δικούς του μετρητές γραμμών του generator, αντί να επιδιορθώνετε παλιές θέσεις. Στην πρόσοψη XLS τα αντίστοιχα στοιχεία ελέγχου βρίσκονται στο Sheet.PageSetup (προσανατολισμός, μέγεθος χαρτιού, περιθώρια, συμβολοσειρές κεφαλίδας και υποσέλιδου, fit-to-pages), με τις RepeatRows και RepeatColumns να καλύπτουν τους τίτλους εκτύπωσης

Έλεγχος του αποτελέσματος πριν το κάνει ο πελάτης

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

Μία τελευταία ρύθμιση ολοκληρώνει την ανασκόπηση. Η FreezePane(ACol, ARow) διατηρεί το μπλοκ κεφαλίδας ορατό ενώ ο ελεγκτής κάνει κύλιση. Αυτό είναι συμπεριφορά οθόνης και όχι συμπεριφορά εκτύπωσης, αλλά ο ελεγκτής κρίνει ολόκληρο το παραδοτέο ταυτόχρονα. Και ένα βιβλίο εργασίας που ξεκινά τη ζωή του ως διάταξη συντηρημένη από σχεδιαστή αποκτά τα περισσότερα από αυτά δωρεάν: η ροή εργασίας δημιουργίας αναφορών από πρότυπο διατηρεί τη διαμόρφωση σελίδας στο πρότυπο, όπου ένας άνθρωπος τη ρύθμισε με βάση έναν πραγματικό εκτυπωτή, και αφήνει τον κώδικα να συμπληρώσει τα δεδομένα και να επανεφαρμόσει την προστασία μόλις σταθεροποιηθεί η διάταξη

Το HotXLS είναι μια εγγενής βιβλιοθήκη υπολογιστικών φύλλων Object Pascal για Delphi και C++Builder· η πλήρης τεκμηρίωση API για προστασία και διαμόρφωση σελίδας βρίσκεται στη σελίδα προϊόντος HotXLS Delphi Component