Artículo técnico

HotXLS: protección de hojas, página e impresión en Delphi

Tres grupos de opciones de hoja de cálculo no tienen nada que ver con los valores de las celdas y todo que ver con cómo se comporta el archivo una vez que sale de su código. La protección de hoja decide qué celdas puede editar un usuario después de que usted entrega el libro de trabajo. La configuración de página fija la orientación, el tamaño de papel y los márgenes. Las opciones de impresión (filas de título repetidas, escala y saltos de página manuales) controlan cómo cae en el papel una cuadrícula de largo arbitrario. Ninguno de los tres aparece cuando usted mira los datos en un visor, y los tres fallan en silencio en campo cuando están mal. HotXLS, una biblioteca nativa de hojas de cálculo para Delphi y C++Builder, expone toda la superficie para .xls y .xlsx, lo que significa que también reproduce cada regla contraintuitiva de Excel incrustada en esa superficie

La primera de esas reglas hace tropezar a casi todo el mundo la primera vez que protege una hoja generada. Llame a Protect y de repente nadie puede escribir en ninguna celda, incluidas las columnas de entrada alrededor de las cuales construyó el libro de trabajo. Nada en su código tocó esas columnas, y por eso mismo ocurre

Cada celda nace bloqueada

ECMA-376 define locked como parte del registro de formato de la celda, no como una propiedad de la protección en sí, y su valor predeterminado es verdadero. La protección de hoja es apenas el interruptor que vuelve exigible esa bandera. Así que toda la cuadrícula lleva una bandera de bloqueo desde el momento en que existe, latente, y la llamada a Protect las activa todas de una vez. La solución es fijar el orden a propósito: construya el diseño, desbloquee de forma explícita los rangos que los usuarios deben editar y proteja al final

Diagrama del orden de protección de HotXLS en Delphi donde cada celda nace con locked en true, los rangos de entrada se desbloquean primero con SetLocked y la llamada final a Sheet.Protect mantiene editables las celdas desbloqueadas
Las celdas llegan bloqueadas de forma predeterminada, así que desbloquee primero los rangos de entrada y llame a Protect al final para mantenerlos editables
Book := TXLSXWorkbook.Create;
try
  Sheet := Book.Sheets.Add('Timesheet');
  // ... aquí se escriben la fila de encabezado, la columna de nombres y las fórmulas de tarifas ...
  Sheet.Range['B2:B50'].SetLocked(False);         // el personal escribe aquí las horas
  Sheet.Range['F2:F50'].SetFormulaHidden(True);   // mantiene privado el cálculo de tarifas
  Sheet.Protect('review-2026');                   // ahora las banderas de bloqueo hacen efecto
  Book.SaveAs('timesheet.xlsx');
finally
  Book.Free;
end;

SetFormulaHidden hace algo distinto y fácil de pasar por alto: mientras la protección está activa, la celda sigue mostrando su valor calculado, pero la barra de fórmulas no muestra nada. Eso importa cuando una fórmula incrusta tarifas de facturación, márgenes o pesos de puntuación que usted preferiría no entregar a cada destinatario que hace clic sobre un total. En la fachada XLS la misma intención se expresa por rango mediante IXLSRange.Locked y FormulaHidden. La hoja de cálculo allí también lleva quince banderas Allow* (AllowSort, AllowAutoFilter, AllowFormatCells y las demás), así que una hoja protegida todavía se puede ordenar y filtrar en lugar de quedar congelada como una pieza sellada

Qué protege en realidad la contraseña de protección

Ambos formatos guardan la contraseña de protección de hoja y de libro de trabajo como un hash heredado de 4 dígitos hexadecimales. Dieciséis bits significan que innumerables cadenas colisionan con cualquier contraseña dada, y las herramientas para quitarla están a una búsqueda de distancia. Trate la protección como un cinturón de seguridad contra ediciones accidentales, no como control de acceso. Es la herramienta correcta para impedir que los revisores escriban encima de la columna de fórmulas y la herramienta equivocada para cualquier cosa que involucre la palabra confidencial

Un nivel más arriba, ProtectWorkbook en la fachada XLSX bloquea la estructura del libro de trabajo, lo que impide agregar, renombrar, eliminar o reordenar hojas. Actívelo siempre que la lista de hojas sea en sí misma un contrato con un analizador aguas abajo que indexa las hojas por nombre o por posición. Una hoja renombrada rompe la importación del otro lado con la misma seguridad que una columna eliminada. La fachada XLS refleja esa estratificación con TXLSWorkbook.Protect a nivel de libro de trabajo y llamadas a Protect por hoja, más una propiedad isProtected para el código que necesita inspeccionar un archivo heredado antes de modificar nada

Cuando el requisito es confidencialidad de verdad, el mecanismo cambia por completo. SaveAsEncrypted produce un paquete cifrado con AES bajo el esquema Standard Encryption de ECMA-376, tratado a fondo en la guía de salida XLSX protegida con AES, y la fachada heredada XLS escribe y lee archivos .xls cifrados con RC4 mediante EncryptionPassword y la sobrecarga con contraseña de Open. La diferencia no es académica. Una hoja protegida viaja en texto claro, así que cualquier herramienta zip puede leer los valores de sus celdas, mientras que un paquete cifrado es ilegible sin la contraseña. Una línea de auditoría que dice "el archivo de nómina debe estar protegido" casi siempre significa cifrado, cualquiera que sea el vocabulario que use

Diagrama que contrasta la protección de hoja de HotXLS, que guarda un hash heredado de 16 bits y deja los valores de celda en texto claro legibles por cualquier herramienta zip, con la salida AES de SaveAsEncrypted que permanece ilegible sin la contraseña
La protección de hoja es un cinturón de seguridad contra ediciones accidentales mientras los valores en texto claro siguen siendo legibles, y solo el cifrado AES oculta el contenido

La configuración de página es parte del contrato del documento

El comportamiento de impresión es invisible en pantalla, y por eso se publica roto tan seguido. En el momento en que un cliente imprime el libro de trabajo, o lo exporta a PDF para un auditor, los márgenes, la escala y los títulos repetidos se convierten en requisitos funcionales que nadie probó. En la fachada XLSX estas opciones cuelgan directamente de la hoja de cálculo:

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';     // referencia simple: aquí no va el nombre de la hoja
Sheet.PrintTitleRows := '$1:$1';     // la fila de encabezado se repite en cada página
Sheet.FitToWidth := 1;
Sheet.FitToHeight := 0;              // crece hacia abajo a medida que crecen los datos
Sheet.PrintGridlines := False;

Dos de esas líneas esconden trampas. Las cadenas de encabezado y de pie usan los códigos de formato de Excel: &P para la página actual, &N para el total, con &L, &C y &R para direccionar las tres secciones de forma explícita. La otra trampa es PrintArea, que toma a propósito una referencia de celda simple. HotXLS la guarda sin calificar y antepone el nombre de la hoja cuando escribe el archivo, así que pasar usted mismo 'Timesheet!$A$1:$F$60' produce una referencia doblemente calificada y malformada. La misma precaución aplica una capa más abajo: las áreas de impresión y los títulos de impresión se persisten como los nombres definidos integrados _xlnm.Print_Area y _xlnm.Print_Titles, así que nunca agregue a mano entradas _xlnm.* a través de DefinedNames, o los dos mecanismos pelearán por la misma ranura

Escala que sobrevive a volúmenes de datos de producción

La combinación de FitToWidth := 1 con FitToHeight := 0 se lee como "ajuste siempre las columnas al ancho de una página y luego tome hacia abajo tantas páginas como los datos necesiten", y es el valor predeterminado correcto para cualquier informe cuyo número de filas varía. La trampa es afinar un porcentaje fijo o un par de ajuste a página contra un archivo de prueba de treinta filas: dele a esas mismas opciones seiscientas filas de producción y la salida o estalla en docenas de páginas recortadas o se encoge por debajo de la legibilidad. Escale el ancho, deje crecer el largo y repita la fila de encabezado con PrintTitleRows para que la página diecisiete siga siendo legible por sí sola

Diagrama de la escala de impresión de HotXLS en Delphi con FitToWidth en 1 para que cada página se mantenga de una hoja de ancho, FitToHeight en 0 para que las páginas crezcan hacia abajo, PrintTitleRows repitiendo la banda de encabezado y los saltos de página regenerados después de ClearAllPageBreaks
FitToWidth 1 con FitToHeight 0 mantiene cada página de una hoja de ancho, mientras las filas de título repetidas y los saltos regenerados preservan la legibilidad

Los saltos manuales siguen la misma disciplina de regeneración que todo lo demás en un libro de trabajo generado. AddRowBreak(BeforeRow) inicia una página nueva antes del límite de una sección, pero cuando el generador vuelve a correr y las filas se desplazan, un salto obsoleto cae a la mitad de la tabla. Llame primero a ClearAllPageBreaks y luego vuelva a agregar los saltos calculados a partir de los propios contadores de filas del generador, en lugar de parchar posiciones viejas. En la fachada XLS los controles equivalentes viven en Sheet.PageSetup (orientación, tamaño de papel, márgenes, cadenas de encabezado y de pie, ajuste a páginas), con RepeatRows y RepeatColumns cubriendo los títulos de impresión

Revisar el resultado antes de que lo haga un cliente

Los errores de protección y de impresión comparten una propiedad: son triviales de verificar a mano y casi nunca se verifican. Abra el archivo generado en Excel y dedíquele noventa segundos. Escriba en una celda de entrada y confirme que acepta la pulsación; escriba en una celda bloqueada y confirme que aparece el aviso de protección; compruebe que una fórmula oculta deja en blanco la barra de fórmulas. Después ejecute la vista previa de impresión contra un conjunto de datos de tamaño de producción, no una muestra de treinta filas, y lea el número de páginas, la fila de título repetida y la numeración del pie. La vista previa es el paso que se paga solo, porque la geometría de impresión depende de opciones sin representación en pantalla y, salvo con una impresora física, es el único lugar donde un error de escala llega a hacerse visible

Una última opción redondea la revisión. FreezePane(ACol, ARow) mantiene a la vista el bloque de encabezado mientras un revisor se desplaza. Eso es comportamiento de pantalla y no de impresión, pero un revisor juzga todo el entregable de una sola vez. Y un libro de trabajo que nace como un diseño mantenido por un diseñador obtiene casi todo esto gratis: el flujo de generación de informes a partir de plantillas conserva la configuración de página en la plantilla, donde una persona la ajustó contra una impresora real, y deja al código llenar los datos y volver a aplicar la protección una vez que el diseño se asienta

HotXLS es una biblioteca nativa de hojas de cálculo en Object Pascal para Delphi y C++Builder; la referencia completa de la API de protección y configuración de página está en la página de producto de HotXLS Delphi Component