Artículo técnico

Definir propiedades de documento Excel en Delphi con HotXLS

Una hoja de cálculo lleva dos capas de identidad. Está la cuadrícula de celdas, y están los metadatos del documento que viajan junto a ella: título, autor, empresa, palabras clave, las marcas de tiempo. Excel nunca muestra esa segunda capa en la cuadrícula, y sin embargo es la capa que indexa Windows Search, la que lee SharePoint para titular un documento, y por la que archiva un sistema de gestión de registros. Cuando un libro generado hereda su Autor y su Título de la plantilla a partir de la cual se construyó, cada sistema aguas abajo registra al diseñador de la plantilla como autor de cuatro mil extractos de clientes. Los metadatos no son correctos en ningún sitio y se consultan en todas partes

HotXLS expone esta capa como propiedades ordinarias a nivel de libro en sus dos motores: la fachada BIFF para .xls y la fachada OOXML para .xlsx. Usted lee un campo después de abrir un archivo y escribe un campo antes de guardar uno. La biblioteca decide en qué contenedor físico aterriza el valor. Lo que merece la pena entender antes de escribir un generador es qué campos admite realmente cada formato, dónde viven físicamente esos campos, y la única regla de control que gobierna si un .xlsx registra algún metadato en absoluto

Dos formatos, dos modelos de almacenamiento

La razón por la que una biblioteca de hojas de cálculo necesita dos implementaciones de metadatos, y la razón por la que las herramientas a medio terminar estampan correctamente un formato y olvidan el otro, es que .xls y .xlsx guardan sus propiedades en lugares sin relación. Un libro BIFF las escribe en flujos de archivo compuesto OLE, principalmente el conjunto de propiedades SummaryInformation, anterior al propio Excel, junto con el registro WRITEACCESS dentro del flujo que nombra a quien guardó el archivo por última vez. Un libro OOXML las guarda como partes XML dentro del paquete zip, separadas por finalidad: docProps/core.xml contiene los campos Dublin Core (título, creador, asunto, palabras clave, fechas) y docProps/app.xml contiene los campos a nivel de aplicación, como la empresa y la aplicación generadora, según ECMA-376 Parte 1

HotXLS aplana ambos modelos de almacenamiento en propiedades directas del objeto libro. Nunca abre un flujo de conjunto de propiedades ni edita una parte XML a mano. Asigna cadenas y fechas al libro, y el contenedor correcto se materializa para el formato que guarde

Diagrama de HotXLS para Delphi que compara el almacenamiento BIFF SummaryInformation con las partes docProps de OOXML para las propiedades de documento de Excel
HotXLS aplana dos modelos de almacenamiento sin relación en una única superficie de propiedades del libro — el motor elige el contenedor físico cuando se guarda el archivo

Estampar los libros generados a partir del registro de negocio

En el lado XLSX, TXLSXWorkbook expone Title, Subject, Author, Keywords, Description, Category, LastModifiedBy, Company, Application y AppVersion como cadenas, más Created y Modified como valores TDateTime donde cero significa sin establecer. La regla que cierra el agujero de la herencia es una sola frase: asigne todos los campos en cada ejecución, tomando los valores del registro de negocio en lugar de confiar en lo que la plantilla llevara por casualidad

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open('statement-template.xlsx') <> 1 then
      raise Exception.Create('Template not available');

    // Sobrescriba todos los campos: lo que quede sin tocar
    // se hereda de quien diseñó la plantilla.
    Book.Title := 'Account Statement 2026-06 / ACME Corp';
    Book.Subject := 'Monthly account statement';
    Book.Author := 'Billing Service 4.2';
    Book.LastModifiedBy := 'Billing Service 4.2';
    Book.Company := 'Northwind Financial';
    Book.Category := 'Customer Delivery';
    Book.Keywords := 'statement;billing;2026-06;acct-10024';
    Book.Description := 'Generated document - manual edits are not retained';
    Book.Created := Now;
    Book.Modified := Now;

    Book.SaveAs('statement-10024.xlsx');
  finally
    Book.Free;
  end;
end;

El campo Keywords merece más reflexión de la que suele recibir. La infraestructura de búsqueda lo indexa literalmente, Windows Search, SharePoint y la mayoría de los productos DMS por igual, así que una convención separada por punto y coma que lleve el número de cuenta y el periodo convierte cada libro entregado en un registro localizable sin ninguna consulta a la base de datos. Ese mismo alcance es la pega. Las propiedades viajan con cada copia del archivo, mucho más allá de los controles de acceso del sistema que las escribió, así que los datos personales no tienen cabida ahí

El par de marcas de tiempo tiene una semántica que conviene fijar por política en lugar de dejarla al hábito. Created debería marcar el momento en que su canalización generó el documento y luego quedarse congelado. Modified es el campo que Excel actualiza cada vez que un destinatario guarda el archivo, así que una divergencia entre ambos después de la entrega es una prueba positiva de que alguien editó el libro aguas abajo, lo que zanja más de una disputa sobre de quién son realmente los números de una hoja de cálculo reenviada. Una trampa se esconde en el estado sin establecer: es el valor literal cero, no una excepción ni un null, así que el código de auditoría tiene que comprobar el cero explícitamente. Formatee un TDateTime sin establecer sin esa protección y sus registros se llenarán de una fecha de diciembre de 1899 tan segura como equivocada

DocPropsTouched: el libro que se entrega sin docProps

Un indicador de solo lectura, DocPropsTouched, controla el escritor de propiedades XLSX. Un libro en el que nunca se asignó ninguna propiedad no produce parte docProps alguna; HotXLS se niega a escribir un esqueleto de metadatos vacío. El comportamiento es limpio, y tiene dos consecuencias que conviene tener en cuenta al diseñar

El código de entrada del lado consumidor no debe asumir que core.xml existe en todos los paquetes. Una herramienta que lo exija de forma estricta rechazará archivos mínimos perfectamente válidos. Y si su postura de cumplimiento exige que todo documento saliente lleve al menos una identidad de generador, esa exigencia se convierte en código en lugar de en una propiedad del formato: asigne Application y Author incondicionalmente en la ruta de guardado, ya que un libro sin tocar es del todo legal según la especificación mientras viola en silencio su política

Diagrama de flujo de HotXLS para Delphi que muestra el indicador DocPropsTouched controlando la salida de docProps en los libros XLSX guardados
DocPropsTouched controla el escritor de docProps de XLSX — asigne Application y Author incondicionalmente cuando la política exija identidad de generador

La superficie XLS heredada y la trampa de Comments

La fachada BIFF lleva el conjunto de campos más antiguo y reducido: Title, Subject, Author, Keywords, Comments, Company y Manager, más LastSavedBy, un alias de UserName, que escribe el registro WRITEACCESS que Excel muestra cuando otro usuario tiene el archivo bloqueado

var
  Legacy: IXLSWorkbook;     // interfaz con recuento de referencias: sin Free manual
begin
  Legacy := TXLSWorkbook.Create;
  if Legacy.Open('archive-1999.xls') <= 0 then
    raise Exception.Create('Cannot open archive file');

  Legacy.Title := 'FY1999 ledger (migrated copy)';
  Legacy.Author := 'Archive Migration Batch';
  Legacy.Company := 'Northwind Financial';
  Legacy.Comments := 'Migrated 2026-06-11; source retained in cold storage';
  Legacy.LastSavedBy := 'migration-svc';   // registro WRITEACCESS de BIFF

  Legacy.SaveAs('archive-1999-stamped.xls');
end;

Una colisión de nombres provoca confusiones recurrentes. La propiedad Comments a nivel de documento de aquí es el comentario de texto libre que se muestra en el diálogo de propiedades del archivo. No tiene nada que ver con los comentarios de celda, que son objetos de la capa de dibujo adjuntos a rangos mediante una API completamente distinta. Una revisión de código que acepte «ya escribimos Comments» sin comprobar a cuál se refiere ha aceptado una afirmación sobre la función equivocada, y eso ocurre más a menudo de lo que el nombre compartido sugeriría. Los dos comparten cuatro letras y ni un solo byte de almacenamiento

Leer metadatos en la entrada, y la laguna del sondeo

La lectura es simétrica. Tras Open, las mismas propiedades vuelven pobladas desde el archivo, lo que convierte una auditoría de metadatos de los libros entrantes en un bucle corto

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open(FileName) = 1 then
    begin
      Writeln(Format('%s | title="%s" author="%s" created=%s',
        [ExtractFileName(FileName), Book.Title, Book.Author,
         FormatDateTime('yyyy-mm-dd', Book.Created)]));
      if Book.Created = 0 then
        Writeln('  no creation date recorded');
    end;
  finally
    Book.Free;
  end;
end;

Planifique en torno a una limitación mientras lo hace. No existe un sondeo solo de propiedades. GetSheetNames puede listar hojas sin cargar un libro, pero leer Title o Author supone un Open completo, así que la clasificación de metadatos en un repositorio grande paga el coste de análisis completo en cada archivo. En el lado BIFF puede recortar ese coste para auditorías de solo lectura estableciendo _DisableGraphics en true antes de abrir, lo que omite directamente la capa de dibujo. Encaja en un bucle que solo lee propiedades y estadísticas de celdas, y es exactamente lo equivocado en cuanto la misma instancia pudiera guardar, porque el contenido de dibujo omitido se perdería. Cuando la estructura de hojas por sí sola puede prefiltrar el conjunto, siendo las exportaciones de una sola hoja lo evidente a omitir, las técnicas baratas de nuestro artículo sobre listado de hojas e inspección ligera reducen cuántos archivos llegan a la pasada cara. Y en trabajos de estampado masivo, donde se escriben miles de salidas en lugar de inspeccionarse, los patrones de rendimiento de escritura de nuestro artículo sobre escritura en streaming para trabajos por lotes se trasladan sin cambios, ya que la asignación de propiedades no añade nada medible al tiempo de guardado

Cruzar formatos y contener la fuga

Las propiedades hacen el ciclo de ida y vuelta limpiamente dentro de una única fachada: abra un .xlsx, edítelo, guárdelo, y el conjunto vuelve intacto. Cruzar formatos es donde se rompe la suposición de paridad, porque los conjuntos de campos BIFF y OOXML no se alinean uno a uno. BIFF tiene Manager y no tiene marcas de tiempo; OOXML tiene Category, Description y el par Created/Modified. Un conversor que copie a ciegas pierde lo que el formato de destino no puede contener, así que mapee los campos explícitamente y ponga el mapeo en su lista de comprobación de conversión junto a todo lo demás que no sobrevive al viaje

Mapa de campos de HotXLS para Delphi que muestra qué propiedades de documento de Excel sobreviven a una conversión entre formatos XLS y XLSX
Una copia a ciegas entre formatos pierde todos los campos que el destino no puede contener — mapee explícitamente los conjuntos de propiedades BIFF y OOXML en la lista de comprobación de conversión

La fuga que abre la herencia de plantillas va en la otra dirección: información que nunca pretendió enviar fuera. Nombres de autor, etiquetas de proyectos internos aparcadas en las palabras clave, un título de borrador que nadie aprobó. La disciplina de sobrescribirlo todo del generador de arriba es toda la defensa, y merece la pena verificarla como lo haría un extraño, abriendo el diálogo de Propiedades al que cualquier cliente puede llegar o descomprimiendo el .xlsx y leyendo docProps/core.xml directamente del paquete. Lo que vea ahí es exactamente lo que ve cada indexador aguas abajo

Esa visibilidad aguas abajo es también la razón por la que unos pocos campos merecen más cuidado que el resto. Title, Author, Keywords (que aparecen como Etiquetas) y Comments o Description soportan la mayor parte del peso de indexación en SharePoint y Windows Search. Un Title genuinamente distinto por documento, que lleve el periodo y la cuenta, hace más por la localizabilidad que cualquier esquema de nombres de carpetas apilado encima, y cuesta una asignación por guardado

Las propiedades de documento son el acabado profesional más barato que un libro generado puede llevar, y el defecto más habitualmente entregado cuando nadie se hace cargo de ellas. Ambas superficies de propiedades descritas aquí pertenecen al HotXLS Delphi Component, que las escribe de forma nativa para XLS y XLSX sin automatización de Excel