Artículo técnico

Editar gráficos de Excel sin perder ChartML con HotXLS

HotXLS conserva el ChartML original de un gráfico de Excel importado y lo reproduce byte a byte cuando nada del modelo tipado ha cambiado, de modo que abrir y volver a guardar un libro no elimina silenciosamente las partes de un gráfico que el modelo de objetos no representa. Edita un título, una serie o un eje y HotXLS fusiona estructuralmente los nuevos nodos tipados en el árbol original en lugar de regenerar el gráfico desde cero

Esa distinción —reproducir cuando no se ha tocado, fusionar cuando se ha editado— es lo que hace seguro apuntar una aplicación Delphi a gráficos que un analista construyó en Excel. Regenerar un gráfico a partir de un modelo tipado siempre pierde aquello que el modelo no conoce, y los gráficos de Excel están llenos de cosas que una librería no conoce: estilos personalizados, listas de extensión, ramas de contenido alternativo, formato cualificado por espacios de nombres procedente de complementos

¿Por qué volver a guardar un libro aplana sus gráficos?

Porque la implementación habitual lee lo que entiende y escribe lo que entiende. Todo lo intermedio —un extLst de una build de Excel más reciente, una rama mc:AlternateContent, un efecto de forma para el que el modelo no tiene propiedad— existe en el archivo, no tiene sitio en el modelo de objetos, y desaparece al guardar. El usuario ve un gráfico que ha perdido su degradado, sus etiquetas de datos personalizadas o su efecto 3D, y el libro no sabe explicarlo

Con PreserveUnsupportedParts activado en el libro, HotXLS almacena al importar los bytes UTF-8 originales del gráfico junto con una longitud y una huella de 64 bits del contenido modelado. Si el modelo tipado no ha cambiado al guardar, escribe los bytes originales de vuelta. Sin reordenación de XML, sin deriva de espacios en blanco, sin copia residente UTF-16 del gráfico colgada en memoria para un gráfico que nadie editó

var
  Workbook: TXLSXWorkbook;
  Chart: TXLSXChart;
begin
  Workbook := TXLSXWorkbook.Create(nil);
  try
    Workbook.PreserveUnsupportedParts := True;   // set before Open
    Workbook.Open('quarterly-review.xlsx');
    Chart := Workbook.Sheets[1].Charts[0];   // Sheets[] is 1-based
    if Chart.HasPreservedXml then
      Log('original ChartML retained; untouched charts replay exactly');
    Chart.Title := 'Revenue by region, Q3 2026';  // now the merge path runs
    Workbook.SaveAs('quarterly-review-out.xlsx');
  finally
    Workbook.Free;
  end;
end;

Qué conserva la fusión y de qué es dueño el modelo

La regla es la propiedad. Los nodos públicos del modelo —título, series, cachés, grupos de trazado, ejes— los decide el árbol recién generado. Los subárboles no modelados se trasladan del original. Esa asimetría es deliberada, y es la parte que cuesta un momento asimilar: borrar una serie no debe resucitar la serie vieja del XML conservado, así que cualquier cosa de la que es dueño el modelo se toma del modelo incluso cuando el modelo dice «se ha ido»

El contenido sin dueño se reinsexa junto a los nodos coincidentes junto a los que estaba, y extLst permanece el último dentro de su padre porque ahí es donde lo exige el esquema. Limpiar una propiedad conocida —retirar una marca de negrita, descartar un color— se trata como una limpieza, no como un hueco a rellenar desde el árbol original. Los atributos desconocidos del mismo entorno conservan su sitio

El contenido de compatibilidad de marcado recibe un tratamiento específico. Al analizar, HotXLS se salta mc:Choice y lee mc:Fallback; si esa rama de respaldo envuelve un grupo de trazado que el modelo conoce, la fusión escribe el nuevo contenido tipado de vuelta en el subárbol de respaldo. Sin ese paso, al guardar se emitiría el nuevo grupo de trazado y se dejaría el viejo dentro del respaldo, y el gráfico portaría dos

Excluir un gráfico concreto

ClearPreservedXml descarta el XML original y la huella de un gráfico, de modo que el próximo guardado reconstruye ese gráfico puramente desde el modelo tipado. Es una decisión por gráfico; la propiedad PreserveUnsupportedParts a nivel de libro sigue rigiendo si el estado de preservación se construye al importar

Acude a ella cuando quieres un gráfico limpio en lugar de uno fiel —por ejemplo cuando un gráfico de plantilla porta formato de un tema corporativo antiguo y el objetivo de la operación es descartarlo—. No acudas a ella como reflejo de depuración: un gráfico que se renderiza mal tras una edición es más a menudo una cuestión de modelado que de preservación, y limpiar el XML conservado destruye la evidencia

Gráficos combinados y el segundo eje

AddPlotGroup construye gráficos combinados ordenados, donde cada grupo de trazado conserva su propia familia de gráfico y su propia asignación a eje primario o secundario. Eso es lo que necesita el ubicuo gráfico de negocio: ingresos como columnas en el eje izquierdo, margen porcentual como línea en el derecho

Los gráficos exponen ejes primario y secundario de categorías y de valores más un eje de series, con ajustes tipados de categoría, valor, fecha y eje de series que cubren posiciones, identificadores, cruces, unidades de fecha, intervalos de etiqueta y visibilidad. La API plana anterior —ChartType, AddSeries, la colección plana Series— sigue dirigiéndose al grupo de trazado primario, de modo que el código existente sigue funcionando mientras el modelo subyacente alberga todos los grupos ordenados

var
  Chart: TXLSXChart;
  Line: TXLSXChartPlotGroup;
begin
  // Column chart anchored over rows 1..15, columns 5..12
  Chart := Sheet.Charts.Add(xlsxChartColumn, 'Revenue and margin',
    1, 5, 15, 12);
  Chart.AddSeries('Revenue', 'Data!$A$2:$A$13', 'Data!$B$2:$B$13');
  // Second plot group: a line, read against the secondary value axis
  Line := Chart.AddPlotGroup(xlsxChartLine, xlsxAxisSecondary);
  Line.AddSeries('Margin %', 'Data!$A$2:$A$13', 'Data!$C$2:$C$13');
end;

Antes de que existiera este modelo, abrir y volver a guardar un gráfico combinado lo colapsaba a una sola familia y perdía los ejes secundario, de fecha y de series. Las referencias a fórmulas de gráfico y los valores de serie cacheados ahora también sobreviven a copias del libro y a ciclos de abrir y guardar, y los anclajes de gráfico se emparejan con sus partes de gráfico por identificador de relación —de modo que una aplicación que reordene entradas de relación ya no intercambia contenidos de gráfico entre posiciones de la hoja—

La trampa del manual layout

Si posicionas a mano un área de trazado, un título, un título de eje o una leyenda, escribe las cuatro coordenadas. Office exige x, y, w y h como grupo completo siempre que esté presente cualquiera de ellas, y en caso contrario ignora todo el manualLayout. HotXLS siempre emite el rectángulo completo por ese motivo, y SetPositionAndSize lo fija y lo habilita en una sola llamada

Dos detalles más siguen a Excel en lugar de la libertad completa de la especificación. La posición se escribe con xMode e yMode a edge mientras que el tamaño usa factor para wMode y hMode, porque eso es lo que escribe Excel y lo que convierte en otras combinaciones. Y layoutTarget se emite únicamente para el área de trazado, nunca para títulos o leyendas, donde las notas de implementación de Microsoft lo prohíben

Encajar esto en un viaje de ida y vuelta del libro

La preservación de gráficos es un caso de una política general: una librería que edita documentos autorizados en otra parte debería cambiar lo que se le pidió cambiar y nada más. El mismo razonamiento impulsa a HotXLS con temas, cadenas de cálculo y extensiones de hoja, descrito en las notas sobre los viajes de ida y vuelta sin pérdida para tema, extLst y calcChain, y sobre proyectos VBA y vínculos externos en la preservación de VBA y vínculos externos. Para la superficie de autoría de gráficos en sí —crear gráficos, colocar imágenes y dibujos desde Delphi— consulta el recorrido por los gráficos, las imágenes y los dibujos en HotXLS

HotXLS lee, edita y escribe gráficos XLSX desde código nativo de Delphi y C++Builder sin Excel instalado en la máquina, que es justamente lo que convierte la fidelidad en un problema de librería en lugar de un problema de automatización; la página del componente de hoja de cálculo HotXLS tiene la lista de características de gráficos y una descarga de prueba