Una parte de gráfico guardada por una build reciente de Excel rara vez se queda en los elementos que el esquema de gráficos de ECMA-376 define. El chart space usualmente abre con un bloque mc:AlternateContent que ofrece c14:style a los lectores nuevos y un c:style simple a los viejos, cada serie carga un c:extLst con un c16:uniqueId, y las etiquetas de datos "Value From Cells" viven en otra extensión más. Un programa que parsea solo lo que modela y regenera la parte al momento de guardar no escribe nada de eso de vuelta. Cambie una palabra del título, reabra el archivo en Excel, y las etiquetas que tomaban texto de un rango de celdas se han ido
El arreglo es dejar de tratar un guardado como una reconstrucción. Conserve los bytes con que llegó el gráfico, escríbalos de vuelta sin cambios cuando nada modelado se haya movido, y cuando algo se haya movido, fusione los elementos nuevos en el árbol original para que las partes desconocidas se queden donde Excel las puso
Reproducir el ChartML original cuando nada cambió
HotXLS hace exactamente eso cuando TXLSXWorkbook.PreserveUnsupportedParts es True, que es el valor predeterminado en un libro de trabajo recién creado. Al importar, cada gráfico conserva sus bytes UTF-8 originales junto con la longitud y una huella digital de 64 bits del contenido que el modelo tipado representa. Al guardar, HotXLS regenera la parte modelada, compara longitud y huella, y si ambas coinciden escribe los bytes almacenados de vuelta tal cual: sin reordenamiento de elementos, sin deriva de espacios en blanco, y sin una segunda copia UTF-16 de cada gráfico intacto colgada en memoria
Como la comparación corre sobre el contenido, asignarle a una propiedad el valor que ya tiene no cambia nada. Los setters de TXLSXChart salen temprano ante un valor igual, así que un ciclo que "refresca" cada título de gráfico desde una fila de base de datos igual reproduce cada gráfico cuyo título ya era correcto. El formato de línea en barras de error y trendlines se rastrea con su propio flag de cambio, y cambiarlo siempre cuenta como una edición
TXLSXChart.HasPreservedXml le dice por qué camino va cada gráfico antes de que usted edite nada. Reporta True cuando hay ChartML original adjunto al gráfico, que es la precondición tanto para la reproducción como para la fusión
var
Workbook: TXLSXWorkbook;
Chart: TXLSXChart;
begin
Workbook := TXLSXWorkbook.Create;
try
// True por defecto; fíjelo explícitamente para que la intención sobreviva a refactorizaciones.
// Tiene que estar en su lugar antes de Open, porque Open construye el estado
Workbook.PreserveUnsupportedParts := True;
Workbook.Open('quarterly-review.xlsx');
Chart := Workbook.Sheets[1].Charts[0]; // Sheets[] tiene base 1, Charts[] base 0
if not Chart.HasPreservedXml then
Log('chart 0 has no original ChartML attached'); // Log es su propia rutina
Chart.Title := Chart.Title; // valor igual: todavía reproduce
Workbook.Sheets[1].Charts[1].Title :=
'Revenue by region, Q3 2026'; // este gráfico toma la ruta de fusión
Workbook.SaveAs('quarterly-review-out.xlsx');
finally
Workbook.Free;
end;
end;
¿Qué sobrevive a una fusión cuando edita un gráfico?
La respuesta corta: todo lo que el modelo tipado no posee. Título, series, cachés de datos, grupos de trazado y ejes son propiedad del modelo, así que el árbol recién generado los decide. Los atributos no modelados, las declaraciones de namespaces, los estilos, las listas de extensión y los nodos de formato vienen del árbol original y se reinsertan junto a los nodos coincidentes junto a los que estaban
Borre una serie y la fusión no debe resucitarla desde el XML preservado solo porque el original todavía contiene un c:ser en esa posición. Todo lo que el modelo posee se toma del modelo incluso cuando la respuesta del modelo es "se fue". Lo mismo aplica a nivel de atributo: cuando usted limpia negrita, itálica, subrayado, tachado o el tamaño de fuente de un estilo de texto conocido, la fusión lee el atributo faltante como una limpieza y no copia b ni sz de vuelta desde el defRPr original. Los atributos desconocidos en el mismo elemento conservan su lugar
Los títulos siguen la misma regla. Al importar, HotXLS conserva las runs rich nativas de c:title, con sus fuentes, tamaños y tags de idioma por run, y las reproduce mientras el texto esté intacto. Asigne un Title nuevo y ese contenido rich se descarta; el título se reescribe como una sola run estilizada desde TitleTextStyle. Fusionar runs viejas en texto nuevo significaría adivinar a qué run pertenecen las palabras nuevas, y una adivinanza aquí produce títulos que nadie escribió
El orden del esquema se preserva donde Office es quisquilloso. Un extLst se queda como último hijo de su padre, como el esquema exige. Los bloques de compatibilidad de marcado reciben tratamiento propio: el lector se salta mc:Choice y lee mc:Fallback, y cuando ese fallback envuelve un grupo de trazado que el modelo conoce, la fusión escribe el contenido tipado nuevo de vuelta en el subárbol del fallback. Una fusión ingenua emitiría el grupo de trazado editado y dejaría la copia vieja dentro de la rama del fallback, así que el gráfico guardado llevaría el mismo grupo dos veces
Excluir un gráfico con ClearPreservedXml
TXLSXChart.ClearPreservedXml suelta el XML original y la huella de un solo gráfico, y el próximo guardado reconstruye ese gráfico puramente desde el modelo tipado. Es una decisión por gráfico. PreserveUnsupportedParts sigue rigiendo si el estado de preservación se construye al importar, y también cubre otras partes de paquete no soportadas, no solo gráficos, razón por la cual apagarlo para conseguir un gráfico limpio es la palanca equivocada
Limpie un gráfico cuando descartar es el punto de la operación, por ejemplo un gráfico de plantilla que todavía carga formato de un tema corporativo retirado. Déjelo tranquilo como reflejo de depuración. Un gráfico que se renderiza mal después de una edición suele ser una pregunta de modelado, y limpiar el XML preservado tira a la basura la única copia de lo que Excel escribió originalmente
Un caso necesita una verificación primero. Si una barra de error o una trendline usa formato de línea que el modelo tipado no puede representar, su propiedad LineAppearanceSupported es False; una etiqueta de trendline con formato fuera del modelo reporta lo mismo vía LabelAppearanceSupported. HotXLS no reemplazará silenciosamente ninguna de las dos por una versión más simple: una vez que el gráfico no tiene XML preservado a donde recurrir, el guardado se detiene con un EArgumentException que nombra la apariencia de línea o etiqueta no representada. Probar ambas antes de limpiar toma un solo ciclo
// Su propio helper, no es parte de HotXLS: True cuando cada línea de barra de error,
// línea de trendline y etiqueta de trendline se pueden escribir solo desde el modelo tipado
function ChartRebuildable(Chart: TXLSXChart): Boolean;
var
I, T: Integer;
S: TXLSXChartSeries;
begin
Result := False;
for I := 0 to Chart.SeriesCount - 1 do // abarca cada grupo de trazado
begin
S := Chart.Series[I];
if (S.ErrBarX <> nil) and not S.ErrBarX.LineAppearanceSupported then Exit;
if (S.ErrBarY <> nil) and not S.ErrBarY.LineAppearanceSupported then Exit;
for T := 0 to S.TrendlineCount - 1 do
begin
if not S.Trendlines[T].LineAppearanceSupported then Exit;
if not S.Trendlines[T].LabelAppearanceSupported then Exit;
end;
end;
Result := True;
end;
// ...
Chart := Workbook.Sheets[1].Charts.FindByName('Old theme chart');
if (Chart <> nil) and ChartRebuildable(Chart) then
Chart.ClearPreservedXml // el próximo guardado reconstruye este gráfico desde el modelo
else
Log('kept original ChartML for this chart');
Gráficos combinados con AddPlotGroup y un eje secundario
TXLSXChart.AddPlotGroup agrega un grupo de trazado ordenado con su propia familia de gráfico y su propia asignación de eje primario o secundario. Eso cubre el gráfico que todo equipo de finanzas pide: ingresos como columnas en el eje izquierdo, porcentaje de margen como línea en el derecho. Cada gráfico expone ValueAxis, CategoryAxis, SecondaryValueAxis, SecondaryCategoryAxis y SeriesAxis, con ajustes tipados de posición, identificadores de eje, cruce, unidades de fecha, intervalos de etiqueta, formato numérico y visibilidad
La API plana de los gráficos de un solo tipo sigue ahí y mapea sobre los grupos. ChartType y AddSeries se dirigen al primer grupo de trazado; SeriesCount y Series[] recorren las series de todos los grupos en orden, razón por la cual el helper de arriba también ve las series de línea. El orden importa en un lugar. TXLSXCharts.Add crea un gráfico sin ningún grupo de trazado, y el que de AddSeries o AddPlotGroup corra primero crea el grupo uno. Llame AddPlotGroup(xlsxChartLine, xlsxAxisSecondary) primero en un gráfico fresco y la línea se vuelve el grupo uno, el tipo de gráfico cambia a línea, y la serie de ingresos que agregue después aterriza en él. Crear ambos grupos explícitamente elimina la ambigüedad, así que el ejemplo hace eso
var
Chart: TXLSXChart;
Columns, Line: TXLSXChartPlotGroup;
begin
// Anclado sobre las filas 1..15, columnas 5..12; aún no existe ningún grupo de trazado
Chart := Sheet.Charts.Add(xlsxChartColumn, 'Revenue and margin',
1, 5, 15, 12);
Columns := Chart.AddPlotGroup(xlsxChartColumn); // grupo 1, primario
Columns.AddSeries('Revenue', 'Data!$A$2:$A$13', 'Data!$B$2:$B$13');
Line := Chart.AddPlotGroup(xlsxChartLine, xlsxAxisSecondary); // grupo 2
Line.AddSeries('Margin %', 'Data!$A$2:$A$13', 'Data!$C$2:$C$13');
Line.ZOrder := 1; // dibuja la línea delante de las columnas
Chart.SecondaryValueAxis.NumberFormat := '0%';
Chart.SecondaryValueAxis.NumberFormatSourceLinked := False;
Chart.SecondaryValueAxis.MinValue := 0;
Chart.SecondaryValueAxis.MaxValue := 0.5;
// Área de trazado como fracciones del gráfico: x, y, ancho, alto en una llamada
Chart.PlotAreaLayout.SetPositionAndSize(0.08, 0.12, 0.80, 0.72);
end;
ZOrder en un grupo de trazado controla el apilado: el escritor de gráficos ordena los grupos por él antes de emitir los bloques c:barChart y c:lineChart, los valores más bajos atrás. Como cada grupo ordenado está modelado, abrir y volver a guardar un gráfico combinado mantiene cada familia de gráfico junto con sus ejes secundario, de fecha y de series. Las referencias a fórmulas de gráfico y los valores de serie cacheados sobreviven a copias del libro de trabajo y a ciclos de apertura y guardado, y los anclajes de gráfico se emparejan con sus partes de gráfico por identificador de relación, así que un paquete cuyas entradas de relación fueron reordenadas por otra herramienta igual pone cada gráfico en su posición de hoja de cálculo
¿Por qué Office ignora un layout manual?
Porque las coordenadas llegaron incompletas. Office exige x, y, w y h como un grupo completo siempre que cualquiera de ellos esté presente en un c:manualLayout, e ignora el elemento entero en caso contrario. El esquema marca los cuatro como opcionales, así que un archivo con solo x y y es válido y aun así no hace nada en Excel
TXLSXChartLayout.SetPositionAndSize es la llamada a usar. Fija los cuatro valores, marca los cuatro elementos de modo como presentes y habilita el layout en un paso. Las propiedades individuales X, Y, Width y Height fijan cada una un valor, y HotXLS escribe exactamente los elementos presentes, de la misma forma en que conserva la presencia de cada uno de los nueve hijos opcionales cuando lee un archivo. Eso mantiene un layout intacto fiel a su fuente. También significa que un layout construido desde dos asignaciones de propiedades se escribe con dos coordenadas, y Office lo va a saltar. La misma clase TXLSXChartLayout está detrás de PlotAreaLayout, TitleLayout, LegendLayout y el TitleLayout de cada eje
Dos valores predeterminados siguen lo que hace Excel en lugar de todo lo que la especificación permite. Un layout nuevo usa edge para XMode y YMode y factor para WidthMode y HeightMode; el estándar permite las otras combinaciones, pero Excel guarda posiciones como edges y tamaños como factors y convierte cualquier otra cosa cuando guarda. Y layoutTarget (Target, inner u outer) se escribe para el área de trazado, mientras los layouts de título, título de eje y leyenda lo omiten, porque las notas de implementación de Microsoft para el esquema de gráficos no lo permiten bajo esos elementos
Cómo encajar esto en un round trip del libro de trabajo
La preservación de gráficos es un caso de una regla más amplia: una librería que edita documentos autorados en otro lado debería cambiar lo que se le pidió cambiar y dejar el resto como lo encontró. HotXLS aplica el mismo razonamiento a temas, cadenas de cálculo y extensiones de hoja de cálculo, cubierto en round trips sin pérdida para theme, extLst y calcChain, y a proyectos VBA y vínculos externos en preservación de VBA y vínculos externos. Para crear gráficos, colocar imágenes y dibujar shapes desde cero, vea gráficos, imágenes y 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, así que la fidelidad de los gráficos la decide la librería y no un servidor de automatización. La referencia de la API de gráficos y una build de prueba están en la página del componente de hojas de cálculo HotXLS para Delphi