Artículo técnico

Delphi: extraiga resúmenes de documentos de Excel

Cuando se procesa un gran volumen de hojas de cálculo de Excel en una canalización automatizada, casi nunca conviene cargar el documento completo en memoria solo para descubrir de qué trata. En muchos casos, los metadatos incorporados en el archivo, como autor, título, fecha de creación y propiedades personalizadas, alcanzan para enrutar, indexar o descartar el documento. En el entorno de Microsoft Office, ese conjunto se conoce como Document Summary Information (información de resumen del documento)

Extraer esa información de forma nativa en Delphi, sin depender de automatización OLE ni de tener Excel instalado en la máquina anfitriona, exige leer directamente la estructura interna del archivo. En este artículo verá cómo funcionan los resúmenes de documentos en los archivos de Excel y cómo extraerlos de forma eficiente mediante análisis directo de flujos

Cómo funcionan los flujos de metadatos de Excel

En los archivos de Excel más antiguos (.xls), los datos se guardan en un formato de documento compuesto OLE que actúa como un sistema de archivos en miniatura con flujos y almacenes. Los metadatos viven en dos flujos concretos:

  • SummaryInformation: contiene propiedades estándar como título, asunto, autor, palabras clave y número de revisión
  • DocumentSummaryInformation: contiene propiedades extendidas como empresa, gerente y propiedades personalizadas definidas por el usuario

Los archivos modernos de Excel (.xlsx) usan el formato Office Open XML (OOXML), una estructura XML comprimida. En ese caso, los metadatos se encuentran en docProps/core.xml, docProps/app.xml y docProps/custom.xml. Un componente sólido de análisis en Delphi debe reconocer sin problemas ambas estructuras y ofrecer una API unificada al desarrollador

Análisis de documentos compuestos OLE en Delphi

Para leer SummaryInformation de un archivo .xls heredado sin depender de herramientas de terceros, hace falta analizar el almacenamiento estructurado OLE. Microsoft expone esa capacidad mediante la interfaz COM IPropertySetStorage. A continuación se muestra una implementación directa en Delphi que evita iniciar Excel:

uses
  System.SysUtils, System.Win.ComObj, Winapi.ActiveX, Winapi.Windows;

procedure ExtractXlsSummaryInfo(const FileName: string);
var
  Stg: IStorage;
  PropSetStg: IPropertySetStorage;
  PropStg: IPropertyStorage;
  PropSpec: TPropSpec;
  PropVariant: TPropVariant;
  Hr: HRESULT;
begin
  // Open the OLE Compound Document
  Hr := StgOpenStorage(PWideChar(WideString(FileName)), nil,
    STGM_READ or STGM_SHARE_DENY_WRITE, nil, 0, Stg);
    
  if Failed(Hr) then
    raise Exception.Create('Failed to open OLE storage. File may not be a valid .xls document.');

  // Query for the property set storage interface
  if Stg.QueryInterface(IPropertySetStorage, PropSetStg) = S_OK then
  begin
    // Open the SummaryInformation stream (FMTID_SummaryInformation)
    Hr := PropSetStg.Open(FMTID_SummaryInformation, STGM_READ or STGM_SHARE_EXCLUSIVE, PropStg);
    if Succeeded(Hr) then
    begin
      // Read the Author property (PIDSI_AUTHOR = 4)
      PropSpec.ulKind := PRSPEC_PROPID;
      PropSpec.propid := PIDSI_AUTHOR;
      
      if PropStg.ReadMultiple(1, @PropSpec, @PropVariant) = S_OK then
      begin
        if PropVariant.vt = VT_LPSTR then
          Writeln('Author: ', string(AnsiString(PropVariant.pszVal)));
        PropVariantClear(PropVariant);
      end;
    end;
  end;
end;

Extracción programática con HotXLS

La API COM de Windows funciona para archivos .xls, pero no para los .xlsx modernos, que en realidad son archivos ZIP. Además, llevar esa ruta COM a un entorno multiplataforma, como Linux o macOS con FireMonkey, no es viable. Las versiones recientes de HotXLS incorporaron unidades dedicadas, por ejemplo lxXlsSummary, para aislar y optimizar la lectura de esos flujos de resumen en ambos formatos de manera nativa en Delphi

Un ejemplo multiplataforma

Con las interfaces XlsReadDocumentSummaryInformation y XlsReadSummaryInformation, puede capturar rápidamente las cadenas de metadatos tanto de .xls como de .xlsx sin preocuparse por la arquitectura interna del archivo

uses
  lxXlsSummary;

var
  Summary: TXlsSummaryInfo;
  ExtendedInfo: TXlsDocumentSummaryInfo;
begin
  // Extract standard summary from an OOXML format seamlessly
  Summary := XlsReadSummaryInformation('C:\Data\FinancialReport.xlsx');
  try
    Writeln('Title: ', Summary.Title);
    Writeln('Author: ', Summary.Author);
    Writeln('Creation Date: ', DateTimeToStr(Summary.CreateTime));
  finally
    Summary.Free;
  end;

  // Extract extended document summary
  ExtendedInfo := XlsReadDocumentSummaryInformation('C:\Data\FinancialReport.xlsx');
  try
    Writeln('Company: ', ExtendedInfo.Company);
    Writeln('Manager: ', ExtendedInfo.Manager);
  finally
    ExtendedInfo.Free;
  end;
end;

Por qué conviene extraer resúmenes de forma dedicada

La ventaja principal de este enfoque es el rendimiento y la seguridad de memoria. Si evita crear todo el modelo de objetos del libro y se limita a leer docProps/core.xml o los flujos de propiedades OLE, su aplicación se mantiene mucho más liviana. Cuando se indexan 10 000 archivos de Excel en un recurso compartido de red, intentar analizarlos por completo agota la memoria y consume horas. La extracción dedicada hace el mismo trabajo en segundos

Además, leer los flujos de forma nativa permite ejecutar la aplicación como servicio en segundo plano o en un servidor Linux sin interfaz gráfica, sin invocar nunca a Excel.exe, algo clave para arquitecturas modernas y escalables

Nota: Las herramientas completas para analizar y extraer metadatos de Excel están disponibles en el HotXLS VCL Component