Una parte di grafico salvata da una build Excel recente raramente si ferma agli elementi che lo schema grafico di ECMA-376 definisce. Il chart space di solito si apre con un blocco mc:AlternateContent che offre c14:style ai lettori più nuovi e un c:style semplice a quelli più vecchi, ogni serie porta una c:extLst con un c16:uniqueId, e le etichette dati "Value From Cells" vivono in un'altra estensione ancora. Un programma che parsa solo ciò che modella e rigenera la parte al momento del salvataggio non riscrive niente di tutto ciò. Cambia una parola nel titolo, riapri il file in Excel, e le etichette che tiravano testo da un intervallo di celle sono sparite
La soluzione è smettere di trattare un salvataggio come una ricostruzione. Conserva i byte con cui il grafico è arrivato, riscrivilili invariati quando nulla di modellato si è mosso, e quando qualcosa si è mosso fondi i nuovi elementi nell'albero originale, così le parti sconosciute restano dove Excel le ha messe
Riprodurre il ChartML originale quando nulla è cambiato
HotXLS fa esattamente questo quando TXLSXWorkbook.PreserveUnsupportedParts è True, che è il default su una cartella di lavoro appena creata. All'importazione ogni grafico conserva i suoi byte UTF-8 originali insieme alla lunghezza e a una fingerprint a 64 bit del contenuto che il modello tipizzato rappresenta. Al salvataggio HotXLS rigenera la parte modellata, confronta lunghezza e fingerprint, e se entrambe corrispondono riscrive i byte conservati alla lettera: nessun riordino di elementi, nessuna deriva di whitespace, e nessuna seconda copia UTF-16 di ogni grafico intatto tenuta in memoria
Siccome il confronto gira sul contenuto, assegnare a una proprietà il valore che già tiene non cambia niente. I setter di TXLSXChart escono presto su un valore uguale, quindi un ciclo che "aggiorna" ogni titolo di grafico da una riga di database riproduce comunque ogni grafico il cui titolo era già corretto. La formattazione delle linee su barre di errore e trendline è tracciata dal proprio flag di modifica, e modificarla conta sempre come una modifica
TXLSXChart.HasPreservedXml ti dice su quale percorso sta un grafico prima che tu modifichi qualsiasi cosa. Riporta True quando al grafico è attaccato il ChartML originale, che è la precondizione sia del replay sia del merge
var
Workbook: TXLSXWorkbook;
Chart: TXLSXChart;
begin
Workbook := TXLSXWorkbook.Create;
try
// True per default; impostalo esplicitamente così l'intenzione sopravvive al refactoring.
// Deve essere a posto prima di Open, perché Open costruisce lo stato
Workbook.PreserveUnsupportedParts := True;
Workbook.Open('quarterly-review.xlsx');
Chart := Workbook.Sheets[1].Charts[0]; // Sheets[] è in base 1, Charts[] in base 0
if not Chart.HasPreservedXml then
Log('chart 0 has no original ChartML attached'); // Log è una tua routine
Chart.Title := Chart.Title; // valore uguale: riproduce comunque
Workbook.Sheets[1].Charts[1].Title :=
'Revenue by region, Q3 2026'; // questo grafico prende il percorso di merge
Workbook.SaveAs('quarterly-review-out.xlsx');
finally
Workbook.Free;
end;
end;
Cosa sopravvive a un merge quando modifichi un grafico?
La risposta breve: tutto ciò che il modello tipizzato non possiede. Titolo, serie, cache dei dati, plot group e assi sono di proprietà del modello, quindi è l'albero appena generato a decidere. Attributi non modellati, dichiarazioni di namespace, stili, liste di estensione e nodi di formattazione arrivano dall'albero originale e vengono reinseriti accanto ai nodi corrispondenti accanto a cui stavano
Cancellare una serie e il merge non deve resuscitarla dall'XML preservato solo perché l'originale contiene ancora una c:ser in quella posizione. Qualsiasi cosa il modello possiede viene presa dal modello anche quando la risposta del modello è "sparita". Lo stesso vale a livello di attributo: quando ripulisci grassetto, corsivo, sottolineato, barrato o la dimensione del font di uno stile di testo noto, il merge legge l'attributo mancante come una cancellazione e non ricopia b o sz dal defRPr originale. Gli attributi sconosciuti sullo stesso elemento conservano il loro posto
I titoli seguono la stessa regola. All'importazione HotXLS conserva le run rich native di c:title, con i loro font, dimensioni e tag di lingua per singola run, e le riproduce finché il testo non è toccato. Assegni un nuovo Title e quel contenuto rich viene buttato; il titolo viene riscritto come una run sola con lo stile di TitleTextStyle. Fondere le vecchie run nel nuovo testo significherebbe indovinare a quale run appartengano le nuove parole, e un'azzardo di questo tipo produce titoli che nessuno ha scritto
L'ordine dello schema viene preservato dove Office è pignolo. Un extLst resta l'ultimo figlio del proprio genitore, come lo schema richiede. I blocchi markup-compatibility ricevono una gestione a parte: il lettore salta mc:Choice e legge mc:Fallback, e quando quel fallback avvolge un plot group che il modello conosce, il merge riscrive il nuovo contenuto tipizzato dentro il sottoalbero di fallback. Un merge ingenuo emetterebbe il plot group modificato e lascerebbe la copia vecchia dentro il ramo di fallback, così il grafico salvato porterebbe lo stesso gruppo due volte
Escludere un solo grafico con ClearPreservedXml
TXLSXChart.ClearPreservedXml butta via l'XML originale e la fingerprint di un solo grafico, e il prossimo salvataggio ricostruisce quel grafico puramente dal modello tipizzato. È una decisione per grafico. PreserveUnsupportedParts continua a governare se lo stato di preservazione viene costruito all'importazione, e copre anche altre parti di pacchetto non supportate, non solo grafici, ed è per questo che spegnerlo per ottenere un grafico pulito è la leva sbagliata
Pulisci un grafico quando lo scarto è il punto dell'operazione, per esempio un grafico da template che porta ancora formattazione di un tema aziendale ritirato. Lascialo stare come riflesso di debug. Un grafico che si renderizza male dopo una modifica di solito è una questione di modellazione, e cancellare l'XML preservato butta via l'unica copia di ciò che Excel aveva scritto originalmente
Un caso richiede prima un controllo. Se una barra di errore o una trendline usa una formattazione di linea che il modello tipizzato non sa rappresentare, la sua proprietà LineAppearanceSupported è False; un'etichetta di trendline con formattazione fuori dal modello riporta lo stesso attraverso LabelAppearanceSupported. HotXLS non sostituisce in silenzio né l'una né l'altra con una versione più povera: una volta che il grafico non ha più XML preservato su cui ricadere, il salvataggio si ferma con una EArgumentException che nomina l'aspetto di linea o etichetta non rappresentato. Testare entrambe le cose prima di pulire richiede un ciclo
// Un tuo helper, non fa parte di HotXLS: True quando ogni linea di barra di errore,
// linea di trendline ed etichetta di trendline si possono scrivere dal solo modello tipizzato
function ChartRebuildable(Chart: TXLSXChart): Boolean;
var
I, T: Integer;
S: TXLSXChartSeries;
begin
Result := False;
for I := 0 to Chart.SeriesCount - 1 do // copre ogni plot group
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 // il prossimo salvataggio ricostruisce questo grafico dal modello
else
Log('kept original ChartML for this chart');
Grafici combinati con AddPlotGroup e un asse secondario
TXLSXChart.AddPlotGroup accoda un plot group ordinato con la propria famiglia di grafico e la propria assegnazione di asse primario o secondario. Copre il grafico che ogni team finance chiede: ricavi come colonne sull'asse sinistro, percentuale di margine come linea sulla destra. Ogni grafico espone ValueAxis, CategoryAxis, SecondaryValueAxis, SecondaryCategoryAxis e SeriesAxis, con impostazioni tipizzate per posizione, identificatori di asse, intersezione, unità di data, intervalli di etichetta, formato numerico e visibilità
L'API piatta dei grafici a tipo singolo esiste ancora e si mappa sui gruppi. ChartType e AddSeries si rivolgono al primo plot group; SeriesCount e Series[] percorrono le serie di ogni gruppo in ordine, ed è per questo che l'helper di sopra vede anche la serie della linea. L'ordine conta in un punto. TXLSXCharts.Add crea un grafico senza nessun plot group, e la prima tra AddSeries e AddPlotGroup che gira crea il gruppo uno. Chiami prima AddPlotGroup(xlsxChartLine, xlsxAxisSecondary) su un grafico fresco e la linea diventa il gruppo uno, il tipo del grafico passa a line, e la serie dei ricavi che aggiungi dopo atterra lì dentro. Creare entrambi i gruppi esplicitamente toglie l'ambiguità, così fa l'esempio
var
Chart: TXLSXChart;
Columns, Line: TXLSXChartPlotGroup;
begin
// Ancorato sulle righe 1..15, colonne 5..12; non esiste ancora nessun plot group
Chart := Sheet.Charts.Add(xlsxChartColumn, 'Revenue and margin',
1, 5, 15, 12);
Columns := Chart.AddPlotGroup(xlsxChartColumn); // gruppo 1, primario
Columns.AddSeries('Revenue', 'Data!$A$2:$A$13', 'Data!$B$2:$B$13');
Line := Chart.AddPlotGroup(xlsxChartLine, xlsxAxisSecondary); // gruppo 2
Line.AddSeries('Margin %', 'Data!$A$2:$A$13', 'Data!$C$2:$C$13');
Line.ZOrder := 1; // disegna la linea davanti alle colonne
Chart.SecondaryValueAxis.NumberFormat := '0%';
Chart.SecondaryValueAxis.NumberFormatSourceLinked := False;
Chart.SecondaryValueAxis.MinValue := 0;
Chart.SecondaryValueAxis.MaxValue := 0.5;
// Area del tracciato come frazioni del grafico: x, y, larghezza, altezza in una chiamata
Chart.PlotAreaLayout.SetPositionAndSize(0.08, 0.12, 0.80, 0.72);
end;
ZOrder su un plot group controlla la stratificazione: lo scrittore del grafico ordina i gruppi per esso prima di emettere i blocchi c:barChart e c:lineChart, valori più bassi dietro. Siccome ogni gruppo ordinato è modellato, aprire e risalvare un grafico combinato tiene ogni famiglia di grafico insieme ai propri assi secondari, di data e di serie. I riferimenti alle formule dei grafici e i valori cached delle serie sopravvivono alle copie della cartella di lavoro e ai cicli di apertura/salvataggio, e gli ancoraggi dei grafici vengono abbinati alle proprie parti grafico per identificatore di relazione, così un pacchetto le cui voci di relazione sono state riordinate da un altro strumento mette comunque ogni grafico alla sua posizione di foglio di lavoro
Perché Office ignora un layout manuale?
Perché le coordinate sono arrivate incomplete. Office richiede x, y, w e h come gruppo completo ogni volta che uno qualsiasi di essi è presente in un c:manualLayout, e ignora l'intero elemento altrimenti. Lo schema segna tutti e quattro come opzionali, quindi un file con solo x e y è valido e non fa comunque niente in Excel
TXLSXChartLayout.SetPositionAndSize è la chiamata da usare. Imposta tutti e quattro i valori, segna i quattro elementi di modalità come presenti e abilita il layout in un passo. Le singole proprietà X, Y, Width e Height impostano ognuna un valore, e HotXLS scrive esattamente gli elementi presenti, allo stesso modo con cui conserva la presenza di ognuno dei nove figli opzionali quando legge un file. Questo tiene un layout intatto fedele alla sua sorgente. Significa anche che un layout costruito con due assegnazioni di proprietà viene scritto con due coordinate, e Office lo salterà. La stessa classe TXLSXChartLayout sta dietro a PlotAreaLayout, TitleLayout, LegendLayout e al TitleLayout di ogni asse
Due default seguono ciò che fa Excel anziché tutto ciò che la specifica permette. Un layout nuovo usa edge per XMode e YMode e factor per WidthMode e HeightMode; lo standard permette le altre combinazioni, ma Excel salva le posizioni come edge e le dimensioni come factor e converte tutto il resto quando salva. E layoutTarget (Target, interno o esterno) viene scritto per l'area del tracciato, mentre i layout di titolo, titolo di asse e legenda lo omettono, perché le note di implementazione Microsoft per lo schema grafico non lo permettono sotto quegli elementi
Inserire questo in un round trip della cartella di lavoro
La preservazione dei grafici è un caso di una regola più ampia: una libreria che modifica documenti scritti altrove dovrebbe cambiare ciò che le è stato chiesto di cambiare e lasciare il resto com'era. HotXLS applica lo stesso ragionamento a temi, catene di calcolo ed estensioni dei fogli di lavoro, trattato nei round trip lossless per tema, extLst e calcChain, e a progetti VBA e collegamenti esterni in preservare VBA e collegamenti esterni. Per creare grafici, collocare immagini e disegnare forme da zero, vedi grafici, immagini e disegni in HotXLS
HotXLS legge, modifica e scrive grafici XLSX da codice Delphi e C++Builder nativo senza Excel installato sulla macchina, così la fedeltà dei grafici è decisa dalla libreria e non da un server di automazione. Il riferimento dell'API dei grafici e una build di prova sono sulla pagina del componente foglio di calcolo Delphi HotXLS