Technischer Artikel

HotXLS: Blattschutz, Seiteneinrichtung und Druck in Delphi

Drei Gruppen von Arbeitsblatt-Einstellungen haben nichts mit den Zellwerten zu tun, dafür alles damit, wie sich die Datei verhält, sobald sie Ihren Code verlassen hat. Der Blattschutz entscheidet, welche Zellen ein Nutzer nach der Übergabe der Arbeitsmappe bearbeiten kann. Die Seiteneinrichtung legt Ausrichtung, Papierformat und Ränder fest. Die Druckeinstellungen (wiederholte Titelzeilen, Skalierung und manuelle Seitenumbrüche) bestimmen, wie ein Raster beliebiger Länge auf Papier landet. Keine der drei ist sichtbar, wenn man die Daten in einem Viewer überfliegt, und alle drei versagen im Feld still, wenn sie falsch gesetzt sind. HotXLS, eine native Tabellenkalkulationsbibliothek für Delphi und C++Builder, stellt die vollständige Oberfläche für .xls und .xlsx bereit, was bedeutet, dass sie auch jede kontraintuitive Excel-Regel reproduziert, die in dieser Oberfläche steckt

Über die erste dieser Regeln stolpert fast jeder, der zum ersten Mal ein generiertes Blatt schützt. Man ruft Protect auf, und plötzlich kann niemand mehr in irgendeine Zelle tippen, auch nicht in die Eingabespalten, um die herum die Arbeitsmappe gebaut wurde. Nichts im Code hat diese Spalten angefasst, und genau deshalb passiert es

Jede Zelle wird gesperrt geboren

ECMA-376 definiert locked als Teil des Formatierungsdatensatzes einer Zelle, nicht als Eigenschaft des Schutzes selbst, und der Standardwert ist true. Der Blattschutz ist lediglich der Schalter, der das Flag durchsetzbar macht. Das gesamte Raster trägt also von dem Moment an, in dem es existiert, ein ruhendes Sperr-Flag, und der Aufruf von Protect aktiviert alle auf einmal. Die Lösung ist eine bewusste Reihenfolge: das Layout aufbauen, die Bereiche, die Nutzer bearbeiten müssen, explizit entsperren und zuletzt schützen

Diagramm der HotXLS-Schutzreihenfolge in Delphi, in der jede Zelle mit locked true entsteht, Eingabebereiche zuerst mit SetLocked entsperrt werden und ein zuletzt aufgerufenes Sheet.Protect die entsperrten Zellen editierbar hält
Zellen sind standardmäßig gesperrt, daher zuerst die Eingabebereiche entsperren und Protect zuletzt aufrufen, damit sie editierbar bleiben
Book := TXLSXWorkbook.Create;
try
  Sheet := Book.Sheets.Add('Timesheet');
  // ... Kopfzeile, Namensspalte und Satzformeln werden hier geschrieben ...
  Sheet.Range['B2:B50'].SetLocked(False);         // Mitarbeiter tragen hier Stunden ein
  Sheet.Range['F2:F50'].SetFormulaHidden(True);   // die Satzberechnung bleibt privat
  Sheet.Protect('review-2026');                   // jetzt greifen die Sperr-Flags
  Book.SaveAs('timesheet.xlsx');
finally
  Book.Free;
end;

SetFormulaHidden tut etwas Eigenständiges und leicht zu Übersehendes: Solange der Schutz aktiv ist, zeigt die Zelle weiterhin ihren berechneten Wert, die Bearbeitungsleiste aber nichts. Das zählt, wenn eine Formel Abrechnungssätze, Margen oder Bewertungsgewichte enthält, die man nicht jedem Empfänger überlassen möchte, der auf eine Summe klickt. Auf der XLS-Fassade wird dieselbe Absicht je Bereich über IXLSRange.Locked und FormulaHidden ausgedrückt. Das Arbeitsblatt trägt dort außerdem fünfzehn Allow*-Flags (AllowSort, AllowAutoFilter, AllowFormatCells und die übrigen), sodass ein geschütztes Blatt weiterhin sortiert und gefiltert werden kann, statt zu einem versiegelten Ausstellungsstück zu erstarren

Was das Schutzpasswort tatsächlich schützt

Beide Formate speichern das Blatt- und Arbeitsmappen-Schutzpasswort als Legacy-Hash aus vier Hexziffern. Sechzehn Bit bedeuten, dass unzählige Zeichenketten mit jedem beliebigen Passwort kollidieren, und Entfernungswerkzeuge sind nur eine Suchanfrage entfernt. Schutz sollte als Sicherheitsgurt gegen versehentliche Änderungen verstanden werden, nicht als Zugriffskontrolle. Er ist das richtige Werkzeug, um Prüfer davon abzuhalten, die Formelspalte zu überschreiben, und das falsche Werkzeug für alles, in dem das Wort vertraulich vorkommt

Eine Ebene höher sperrt ProtectWorkbook auf der XLSX-Fassade die Arbeitsmappenstruktur, was das Hinzufügen, Umbenennen, Löschen oder Umsortieren von Blättern verhindert. Man setzt es immer dann, wenn die Blattliste selbst ein Vertrag mit einem nachgelagerten Parser ist, der Blätter nach Name oder Position indiziert. Ein umbenanntes Blatt bricht den Import auf der anderen Seite genauso sicher wie eine gelöschte Spalte. Die XLS-Fassade spiegelt die Schichtung mit TXLSWorkbook.Protect auf Arbeitsmappenebene und Protect-Aufrufen je Blatt wider, ergänzt um eine Eigenschaft isProtected für Code, der eine geerbte Datei prüfen muss, bevor er etwas ändert

Wenn die Anforderung echte Vertraulichkeit ist, ändert sich der Mechanismus vollständig. SaveAsEncrypted erzeugt ein AES-verschlüsseltes Paket nach dem ECMA-376-Schema Standard Encryption, ausführlich behandelt in der Anleitung zur AES-geschützten XLSX-Ausgabe, und die Legacy-XLS-Fassade schreibt und liest RC4-verschlüsselte .xls-Dateien über EncryptionPassword und die Passwort-Überladung von Open. Der Unterschied ist nicht akademisch. Ein geschütztes Blatt reist im Klartext, sodass jedes Zip-Werkzeug seine Zellwerte lesen kann, während ein verschlüsseltes Paket ohne das Passwort unlesbar ist. Eine Prüfvorgabe, die besagt „die Gehaltsdatei muss geschützt sein“, meint fast immer Verschlüsselung, welches Vokabular sie auch verwendet

Diagramm, das den HotXLS-Blattschutz, der einen 16-Bit-Legacy-Hash speichert und Zellwerte im Klartext für jedes Zip-Werkzeug lesbar lässt, der AES-Ausgabe von SaveAsEncrypted gegenüberstellt, die ohne das Passwort unlesbar bleibt
Blattschutz ist ein Sicherheitsgurt gegen versehentliche Änderungen, während Klartextwerte lesbar bleiben, und erst AES-Verschlüsselung verbirgt den Inhalt

Die Seiteneinrichtung ist Teil des Dokumentvertrags

Das Druckverhalten ist auf dem Bildschirm unsichtbar, und genau deshalb wird es so oft defekt ausgeliefert. In dem Moment, in dem ein Kunde die Arbeitsmappe druckt oder für einen Prüfer nach PDF exportiert, werden Ränder, Skalierung und wiederholte Titel zu funktionalen Anforderungen, die niemand getestet hat. Auf der XLSX-Fassade hängen diese Einstellungen direkt am Arbeitsblatt:

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';     // nackte Referenz: hier kein Blattname
Sheet.PrintTitleRows := '$1:$1';     // Kopfzeile wiederholt sich auf jeder Seite
Sheet.FitToWidth := 1;
Sheet.FitToHeight := 0;              // nach unten wachsen, wenn die Daten wachsen
Sheet.PrintGridlines := False;

Zwei dieser Zeilen verbergen Fallen. Die Kopf- und Fußzeilen-Strings verwenden die Formatierungscodes von Excel: &P für die aktuelle Seite, &N für die Gesamtzahl, dazu &L, &C und &R, um die drei Abschnitte explizit anzusprechen. Die andere Falle ist PrintArea, das absichtlich eine nackte Zellreferenz erwartet. HotXLS speichert sie unqualifiziert und stellt beim Schreiben der Datei den Blattnamen voran, sodass ein selbst übergebenes 'Timesheet!$A$1:$F$60' eine doppelt qualifizierte, fehlerhafte Referenz erzeugt. Dieselbe Vorsicht gilt eine Schicht tiefer: Druckbereiche und Drucktitel werden als die eingebauten definierten Namen _xlnm.Print_Area und _xlnm.Print_Titles persistiert, daher sollten _xlnm.*-Einträge niemals von Hand über DefinedNames hinzugefügt werden, sonst streiten die beiden Mechanismen um denselben Platz

Skalierung, die Produktionsdatenmengen übersteht

Die Kombination FitToWidth := 1 mit FitToHeight := 0 liest sich als „die Spalten immer auf eine Seitenbreite einpassen, dann so viele Seiten nach unten nehmen, wie die Daten brauchen“, und sie ist der richtige Standard für jeden Bericht, dessen Zeilenzahl schwankt. Die Falle ist, einen festen Prozentsatz oder ein Seiten-Einpassungspaar an einer Testdatei mit dreißig Zeilen abzustimmen: Füttert man dieselben Einstellungen mit sechshundert Produktionszeilen, explodiert die Ausgabe entweder in Dutzende abgeschnittene Seiten oder schrumpft unter die Lesbarkeitsgrenze. Die Breite skalieren, die Länge wachsen lassen und die Kopfzeile über PrintTitleRows wiederholen, damit Seite siebzehn auch für sich allein lesbar bleibt

Diagramm der HotXLS-Druckskalierung in Delphi mit FitToWidth auf 1, damit jede Seite eine Blattbreite behält, FitToHeight auf 0, damit die Seiten nach unten wachsen, PrintTitleRows zur Wiederholung des Kopfbands und nach ClearAllPageBreaks neu erzeugten Seitenumbrüchen
FitToWidth 1 mit FitToHeight 0 hält jede Seite eine Blattbreite breit, während wiederholte Titelzeilen und neu erzeugte Umbrüche die Lesbarkeit bewahren

Manuelle Umbrüche folgen derselben Regenerationsdisziplin wie alles andere in einer generierten Arbeitsmappe. AddRowBreak(BeforeRow) beginnt vor einer Abschnittsgrenze eine neue Seite, aber wenn der Generator erneut läuft und die Zeilen sich verschieben, landet ein veralteter Umbruch mitten in der Tabelle. Zuerst ClearAllPageBreaks aufrufen, dann die Umbrüche aus den eigenen Zeilenzählern des Generators neu berechnen und hinzufügen, statt alte Positionen zu flicken. Auf der XLS-Fassade liegen die entsprechenden Steuerelemente auf Sheet.PageSetup (Ausrichtung, Papierformat, Ränder, Kopf- und Fußzeilen-Strings, Seiteneinpassung), wobei RepeatRows und RepeatColumns die Drucktitel abdecken

Das Ergebnis prüfen, bevor es ein Kunde tut

Schutz- und Druckfehler haben eine Eigenschaft gemeinsam: Sie sind von Hand trivial zu prüfen und werden fast nie geprüft. Öffnen Sie die generierte Datei in Excel und wenden Sie neunzig Sekunden dafür auf. In eine Eingabezelle tippen und bestätigen, dass sie den Tastendruck annimmt; in eine gesperrte Zelle tippen und bestätigen, dass die Schutzmeldung erscheint; prüfen, dass eine ausgeblendete Formel die Bearbeitungsleiste leer lässt. Dann die Druckvorschau mit einem Datensatz in Produktionsgröße ausführen, nicht mit einer Stichprobe von dreißig Zeilen, und Seitenzahl, wiederholte Titelzeile und Fußzeilennummerierung ablesen. Die Vorschau ist der Schritt, der sich selbst bezahlt, denn die Druckgeometrie hängt von Einstellungen ohne Bildschirmdarstellung ab, und abgesehen von einem physischen Drucker ist sie der einzige Ort, an dem ein Skalierungsfehler je sichtbar wird

Eine letzte Einstellung rundet die Prüfung ab. FreezePane(ACol, ARow) hält den Kopfblock im Blick, während ein Prüfer scrollt. Das ist Bildschirm- und nicht Druckverhalten, aber ein Prüfer beurteilt das gesamte Ergebnis auf einmal. Und eine Arbeitsmappe, die als designergepflegtes Layout beginnt, bekommt das meiste davon geschenkt: Der Workflow zur vorlagenbasierten Berichtserzeugung belässt die Seiteneinrichtung in der Vorlage, wo ein Mensch sie an einem echten Drucker abgestimmt hat, und überlässt dem Code das Befüllen der Daten und das erneute Anwenden des Schutzes, sobald das Layout steht

HotXLS ist eine native Object-Pascal-Tabellenkalkulationsbibliothek für Delphi und C++Builder; die vollständige API-Referenz zu Schutz und Seiteneinrichtung findet sich auf der Produktseite der HotXLS Delphi Component