Article technique

HotXLS : protection, mise en page et impression en Delphi

Trois groupes de réglages de feuille de calcul n'ont rien à voir avec les valeurs des cellules et tout à voir avec le comportement du fichier une fois qu'il a quitté votre code. La protection de feuille décide quelles cellules un utilisateur pourra modifier après que vous lui aurez remis le classeur. La mise en page fixe l'orientation, le format de papier et les marges. Les réglages d'impression (lignes de titre répétées, mise à l'échelle et sauts de page manuels) commandent la façon dont une grille de longueur quelconque se pose sur le papier. Aucun des trois n'apparaît quand vous inspectez les données dans une visionneuse, et les trois cassent en silence sur le terrain quand ils sont faux. HotXLS, bibliothèque tableur native pour Delphi et C++Builder, expose la surface complète pour .xls et .xlsx, ce qui signifie qu'elle reproduit aussi chaque règle Excel contre-intuitive cuite dans cette surface

La première de ces règles fait trébucher presque tout le monde la première fois que l'on protège une feuille générée. Appelez Protect et soudain plus personne ne peut taper dans aucune cellule, y compris les colonnes de saisie autour desquelles vous avez bâti le classeur. Rien dans votre code n'a touché ces colonnes, et c'est exactement pour cela que cela se produit

Chaque cellule naît verrouillée

ECMA-376 définit locked comme une partie de l'enregistrement de mise en forme d'une cellule, non comme une propriété de la protection elle-même, et sa valeur par défaut est vraie. La protection de feuille n'est que l'interrupteur qui rend l'indicateur applicable. Toute la grille porte donc un indicateur de verrou dès son existence, en sommeil, et l'appel à Protect les active tous d'un coup. Le remède consiste à fixer l'ordre délibérément : construisez la disposition, déverrouillez explicitement les plages que les utilisateurs doivent modifier, et protégez en dernier

Diagramme de l'ordre de protection HotXLS en Delphi, où chaque cellule naît avec locked à vrai, où les plages de saisie sont déverrouillées par SetLocked en premier et où Sheet.Protect appelé en dernier garde les cellules déverrouillées modifiables
Les cellules arrivent verrouillées par défaut : déverrouillez donc les plages de saisie en premier et appelez Protect en dernier pour les garder modifiables
Book := TXLSXWorkbook.Create;
try
  Sheet := Book.Sheets.Add('Timesheet');
  // ... ligne d'en-tête, colonne des noms et formules de taux écrites ici ...
  Sheet.Range['B2:B50'].SetLocked(False);         // le personnel saisit ses heures ici
  Sheet.Range['F2:F50'].SetFormulaHidden(True);   // garder privé le calcul du taux
  Sheet.Protect('review-2026');                   // les indicateurs de verrou mordent maintenant
  Book.SaveAs('timesheet.xlsx');
finally
  Book.Free;
end;

SetFormulaHidden fait quelque chose de distinct et facile à manquer : tant que la protection est active, la cellule affiche encore sa valeur calculée, mais la barre de formule ne montre rien. Cela compte quand une formule incorpore des taux de facturation, des marges ou des pondérations de notation que vous préféreriez ne pas remettre à chaque destinataire qui clique sur un total. Sur la façade XLS, la même intention s'exprime plage par plage via IXLSRange.Locked et FormulaHidden. La feuille de calcul y porte aussi quinze indicateurs Allow* (AllowSort, AllowAutoFilter, AllowFormatCells et les autres), si bien qu'une feuille protégée peut encore être triée et filtrée au lieu d'être figée en pièce scellée

Ce que le mot de passe de protection protège réellement

Les deux formats rangent le mot de passe de protection de feuille et de classeur sous forme d'une empreinte héritée de 4 chiffres hexadécimaux. Seize bits signifient que d'innombrables chaînes entrent en collision avec n'importe quel mot de passe donné, et les outils de suppression sont à une recherche de distance. Traitez la protection comme une ceinture de sécurité contre les modifications accidentelles, non comme un contrôle d'accès. C'est le bon outil pour empêcher des relecteurs d'écraser la colonne des formules et le mauvais outil pour tout ce qui met en jeu le mot confidentiel

Un niveau au-dessus, ProtectWorkbook sur la façade XLSX verrouille la structure du classeur, ce qui empêche d'ajouter, de renommer, de supprimer ou de réordonner des feuilles. Activez-le dès que la liste des feuilles est elle-même un contrat avec un analyseur en aval qui indexe les feuilles par nom ou par position. Une feuille renommée casse l'import de l'autre côté tout aussi sûrement qu'une colonne supprimée. La façade XLS reflète cette superposition avec TXLSWorkbook.Protect au niveau du classeur et des appels Protect par feuille, plus une propriété isProtected pour le code qui doit inspecter un fichier hérité avant de modifier quoi que ce soit

Quand l'exigence est une vraie confidentialité, le mécanisme change entièrement. SaveAsEncrypted produit un paquet chiffré en AES sous le schéma de chiffrement standard d'ECMA-376, traité en profondeur dans le guide de la sortie XLSX protégée par AES, et la façade XLS héritée écrit et lit des fichiers .xls chiffrés en RC4 via EncryptionPassword et la surcharge à mot de passe de Open. La différence n'a rien d'académique. Une feuille protégée voyage en clair, si bien que n'importe quel outil zip peut lire ses valeurs de cellules, tandis qu'un paquet chiffré reste illisible sans le mot de passe. Une ligne d'audit qui dit « le fichier de paie doit être protégé » signifie presque toujours chiffrement, quel que soit le vocabulaire employé

Diagramme opposant la protection de feuille HotXLS, qui range une empreinte héritée de 16 bits et laisse les valeurs de cellules en clair lisibles par n'importe quel outil zip, à la sortie AES de SaveAsEncrypted qui reste illisible sans le mot de passe
La protection de feuille est une ceinture de sécurité contre les modifications accidentelles alors que les valeurs en clair restent lisibles, et seul le chiffrement AES cache le contenu

La mise en page fait partie du contrat du document

Le comportement à l'impression est invisible à l'écran, ce qui explique qu'il parte si souvent cassé. Dès qu'un client imprime le classeur, ou l'exporte en PDF pour un auditeur, les marges, la mise à l'échelle et les titres répétés deviennent des exigences fonctionnelles que personne n'a testées. Sur la façade XLSX, ces réglages pendent directement de la feuille de calcul :

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';     // référence nue : pas de nom de feuille ici
Sheet.PrintTitleRows := '$1:$1';     // la ligne d'en-tête se répète sur chaque page
Sheet.FitToWidth := 1;
Sheet.FitToHeight := 0;              // grandir vers le bas à mesure que les données grandissent
Sheet.PrintGridlines := False;

Deux de ces lignes cachent des pièges. Les chaînes d'en-tête et de pied de page utilisent les codes de mise en forme d'Excel : &P pour la page courante, &N pour le nombre total, avec &L, &C et &R pour adresser explicitement les trois sections. L'autre piège est PrintArea, qui prend une référence de cellule nue à dessein. HotXLS la range non qualifiée et préfixe le nom de feuille au moment d'écrire le fichier, si bien que passer vous-même 'Timesheet!$A$1:$F$60' produit une référence doublement qualifiée et mal formée. La même prudence vaut une couche plus bas : les zones d'impression et les titres d'impression sont persistés comme les noms définis intégrés _xlnm.Print_Area et _xlnm.Print_Titles, alors n'ajoutez jamais d'entrées _xlnm.* à la main par DefinedNames, sinon les deux mécanismes se disputeront le même emplacement

Une mise à l'échelle qui survit aux volumes de production

La combinaison de FitToWidth := 1 avec FitToHeight := 0 se lit comme « toujours faire tenir les colonnes sur la largeur d'une page, puis prendre autant de pages en hauteur que les données en demandent », et c'est la valeur par défaut correcte pour tout rapport dont le nombre de lignes varie. Le piège consiste à régler un pourcentage fixe ou un couple d'ajustement à la page sur un fichier de test de trente lignes : donnez aux mêmes réglages six cents lignes de production et la sortie explose en dizaines de pages tronquées ou rétrécit sous le seuil de lisibilité. Mettez la largeur à l'échelle, laissez la longueur grandir, et répétez la ligne d'en-tête par PrintTitleRows afin que la page dix-sept reste lisible à elle seule

Diagramme de la mise à l'échelle d'impression HotXLS en Delphi avec FitToWidth réglé à 1 pour que chaque page reste large d'une feuille, FitToHeight réglé à 0 pour que les pages grandissent vers le bas, PrintTitleRows qui répète le bandeau d'en-tête, et les sauts de page régénérés après ClearAllPageBreaks
FitToWidth à 1 avec FitToHeight à 0 garde chaque page large d'une seule feuille tandis que les lignes de titre répétées et les sauts régénérés préservent la lisibilité

Les sauts manuels suivent la même discipline de régénération que tout le reste dans un classeur généré. AddRowBreak(BeforeRow) commence une nouvelle page avant une limite de section, mais quand le générateur se relance et que les lignes se décalent, un saut périmé atterrit au milieu d'un tableau. Appelez d'abord ClearAllPageBreaks, puis rajoutez les sauts calculés à partir des compteurs de lignes du générateur lui-même au lieu de rapiécer d'anciennes positions. Sur la façade XLS, les commandes équivalentes vivent sur Sheet.PageSetup (orientation, format de papier, marges, chaînes d'en-tête et de pied de page, ajustement au nombre de pages), RepeatRows et RepeatColumns couvrant les titres d'impression

Contrôler le résultat avant qu'un client le fasse

Les bogues de protection et d'impression partagent une propriété : ils sont triviaux à vérifier à la main et ne sont presque jamais vérifiés. Ouvrez le fichier généré dans Excel et passez-y quatre-vingt-dix secondes. Tapez dans une cellule de saisie et confirmez qu'elle accepte la frappe ; tapez dans une cellule verrouillée et confirmez que l'invite de protection apparaît ; vérifiez qu'une formule masquée laisse la barre de formule vide. Lancez ensuite l'aperçu avant impression sur un jeu de données de taille production, pas sur un échantillon de trente lignes, et relevez le nombre de pages, la ligne de titre répétée et la numérotation du pied de page. L'aperçu est l'étape qui se rembourse toute seule, car la géométrie d'impression dépend de réglages sans rendu à l'écran et, à défaut d'imprimante physique, c'est le seul endroit où une erreur de mise à l'échelle devienne jamais visible

Un dernier réglage complète la revue. FreezePane(ACol, ARow) garde le bloc d'en-tête en vue pendant qu'un relecteur fait défiler. C'est un comportement d'écran plutôt que d'impression, mais un relecteur juge tout le livrable d'un bloc. Et un classeur qui commence sa vie comme une mise en page entretenue par un concepteur obtient l'essentiel de tout cela gratuitement : le flux de génération de rapports par modèle garde la mise en page dans le modèle, où un humain l'a réglée face à une vraie imprimante, et laisse au code le soin de remplir les données et de réappliquer la protection une fois la disposition stabilisée

HotXLS est une bibliothèque tableur native en Object Pascal pour Delphi et C++Builder ; la référence complète de l'API de protection et de mise en page se trouve sur la page produit du HotXLS Delphi Component