Teknik Makale

HotXLS ile ChartML Kaybetmeden Excel Grafiklerini Düzenlemek

Güncel bir Excel sürümünün kaydettiği bir grafik parçası, nadiren ECMA-376 grafik şemasının tanımladığı öğelerde durur. Grafik alanı çoğunlukla bir mc:AlternateContent bloğuyla açılır; o blok, yeni okuyuculara c14:style, eski okuyuculara düz bir c:style sunar; her seri bir c:extLst sürer ve içinde c16:uniqueId taşır ve "Value From Cells" veri etiketleri başka bir uzantıda yaşar. Yalnızca modellediğini ayrıştırıp kaydetme anında parçayı yeniden üreten bir program, bunların hiçbirini geri yazmaz. Başlıkta bir kelime değiştirin, dosyayı Excel'de yeniden açın ve bir hücre aralığından metin çeken etiketler gitmiştir

Çözüm, kaydetmeyi yeniden inşa saymaktan vazgeçmektir. Grafiğin geldiği baytları tutun; modelde hiçbir şey değişmemişse onları olduğu gibi geri yazın; değişmişse yeni öğeleri özgün ağaca birleştirin, böylece tanımadığınız parçalar Excel'in bıraktığı yerde kalır

Hiçbir şey değişmediğinde özgün ChartML'yi yeniden oynatmak

HotXLS, TXLSXWorkbook.PreserveUnsupportedParts True iken tam olarak bunu yapar; yeni oluşturulan bir çalışma kitabında bu zaten varsayılandır. İçe aktarmada her grafik, özgün UTF-8 baytlarını, uzunlukla birlikte ve tipleştirilmiş modelin temsil ettiği içeriğin 64 bitlik bir parmak iziyle tutar. Kaydetme anında HotXLS modellenmiş parçayı yeniden üretir, uzunluk ile parmak izini karşılaştırır ve ikisi de eşleşirse saklanan baytları harfiyen geri yazar: öğe sıralaması değişmez, boşluk kayması olmaz ve dokunulmamış her grafiğin ikinci bir UTF-16 kopyası bellekte tutulmaz

Karşılaştırma içerik üzerinde koştuğu için, bir özelliğin hâlihazırda taşıdığı değeri yeniden atamak hiçbir şey değiştirmez. TXLSXChart ayarlayıcıları eşit değerde erken çıkar; dolayısıyla bir veritabanı satırından her grafik başlığını "tazeleyen" bir döngü, başlığı zaten doğru olan her grafiği yine oynatır. Hata çubukları ve trend çizgilerindeki çizgi biçimlendirmesi kendi değişim bayrağıyla izlenir ve onu değiştirmek her zaman bir düzenleme sayılır

Değişmeyen grafiklerin özgün baytları oynattığı, düzenlenen grafiklerde tipleştirilmiş model düğümlerinin korunan XML'e birleştirildiği ve yalnızca modelden yeniden inşanın extLst ile AlternateContent dallarını kaybettiği Delphi'de HotXLS ChartML korunumu şeması
HotXLS, özgün ChartML baytlarını içe aktarmada saklar ve dokunmadan oynatır; model değiştiğinde ise tipleştirilmiş düzenlemeleri korunan ağaca birleştirir

TXLSXChart.HasPreservedXml, herhangi bir şeyi düzenlemeden önce grafiğin hangi yolda olduğunu söyler. Grafiğe özgün ChartML bağlıyken True raporlar; bu, hem oynatmanın hem birleştirmenin ön koşuludur

var
  Workbook: TXLSXWorkbook;
  Chart: TXLSXChart;
begin
  Workbook := TXLSXWorkbook.Create;
  try
    // Varsayılanı True; niyetin yeniden düzenlemeden sağ çıkması için açıkça ayarlayın.
    // Open durumu kurduğu için bu, Open'dan önce yerinde olmalıdır
    Workbook.PreserveUnsupportedParts := True;
    Workbook.Open('quarterly-review.xlsx');

    Chart := Workbook.Sheets[1].Charts[0];   // Sheets[] 1 tabanlıdır, Charts[] 0 tabanlı
    if not Chart.HasPreservedXml then
      Log('chart 0 has no original ChartML attached');   // Log sizin kendi yordamınızdır

    Chart.Title := Chart.Title;                  // eşit değer: yine oynatılır
    Workbook.Sheets[1].Charts[1].Title :=
      'Revenue by region, Q3 2026';              // bu grafik birleştirme yolunu izler
    Workbook.SaveAs('quarterly-review-out.xlsx');
  finally
    Workbook.Free;
  end;
end;

Tek bir grafiği düzenlerken birleştirme neyi tutar?

Kısa cevap: tipleştirilmiş modelin sahip olmadığı her şey. Başlık, seriler, veri önbellekleri, çizim grupları ve eksenler modele aittir; dolayısıyla yeni üretilen ağaç onlara karar verir. Modellenmemiş öznitelikler, ad alanı bildirimleri, stiller, uzantı listeleri ve biçimlendirme düğümleri özgün ağaçtan gelir ve oturdukları eşleşen düğümlerin yanına yeniden yerleştirilir

Bir seriyi silin ve birleştirme, onu korunan XML'den diriltmemelidir; özgün ağaç o konumda hâlâ bir c:ser taşıyorsa bile. Modelin sahip olduğu her şey, modelin cevabı "gitti" olsa bile modelden alınır. Aynı şey öznitelik düzeyinde de geçerlidir: bilinen bir metin stilinin kalın, eğik, altı çizgili, üstü çizgili ya da font boyutu özelliğini temizlediğinizde birleştirme, eksik özniteliği temizlenmiş olarak okur ve b ya da sz'yi özgün defRPr'den geri kopyalamaz. Aynı öğedeki bilinmeyen öznitelikler yerini korur

Başlıklar aynı kuralı izler. İçe aktarmada HotXLS, c:title'nin yerleşik zengin metin çalıştırmalarını (rich runs) run başına fontları, boyutları ve dil etiketleriyle tutar ve metin el değmeden kaldıkça onları oynatır. Yeni bir Title atayın ve o zengin içerik düşer; başlık, TitleTextStyle'den biçimlendirilmiş tek bir run olarak yeniden yazılır. Eski run'ları yeni metne birleştirmek, yeni kelimelerin hangi run'a ait olduğunu tahmin etmek demektir ve buradaki bir tahmin, kimsenin yazmadığı başlıklar üretir

Şema sırası, Office'in üzerinde titiz olduğu yerlerde korunur. Bir extLst, şemanın gerektirdiği gibi ebeveynein son çocuğu kalır. İşaretleme uyumluluk blokları ayrı bir muamele görür: okuyucu mc:Choice'i atlar ve mc:Fallback'i okur; o fallback, modelin tanıdığı bir çizim grubuna sarıldığında birleştirme, yeni tipleştirilmiş içeriği fallback alt ağacının içine geri yazar. Naif bir birleştirme, düzenlenmiş çizim grubunu yayarken eski kopyayı fallback dalının içinde bırakır; kaydedilen grafik aynı grubu iki kez taşır

ClearPreservedXml ile tek bir grafiği hariç tutmak

TXLSXChart.ClearPreservedXml, tek bir grafik için özgün XML'i ve parmak izini düşürür ve bir sonraki kaydetme, o grafiği tamamen tipleştirilmiş modelden yeniden kurar. Bu, grafik başına bir karardır. PreserveUnsupportedParts, korunum durumunun içe aktarmada kurulup kurulmayacağını yönetmeye devam eder ve grafiklerden başka desteklenmeyen paket parçalarını da kapsar; tek bir temiz grafik için onu kapatmak yanlış kaldıraçtır

Bir grafiği, atmak işlemin amacıyken temizleyin; örneğin emekliye ayrılmış kurumsal bir temadan biçimlendirme hâlâ taşıyan bir şablon grafiği. Hata ayıklama refleksiyle dokunmayın. Bir düzenlemeden sonra yanlış çizilen grafik çoğunlukla bir modelleme sorunudur ve korunan XML'i temizlemek, Excel'in baştan yazdığının tek kopyasını çöpe atar

Tek bir durum önce kontrol ister. Bir hata çubuğu ya da trend çizgisi, tipleştirilmiş modelin ifade edemediği bir çizgi biçimlendirmesi kullanıyorsa LineAppearanceSupported özelliği False'tır; modelin dışında biçimlendirmesi olan bir trend çizgisi etiketi aynı şeyi LabelAppearanceSupported ile raporlar. HotXLS ikisini de sessizce daha sade bir sürümle değiştirmez: grafiğin üzerine düşebileceği korunan XML kalmadığında kaydetme, ifade edilemeyen çizgi ya da etiket görünümünü adlandıran bir EArgumentException ile durur. Temizlemeden önce ikisini sınamak bir döngüdür

// Kendi yardımcınız, HotXLS'in parçası değil: her hata çubuğu çizgisi,
// trend çizgisi ve trend etiketi yalnızca tipleştirilmiş modelden yazılabiliyorsa True
function ChartRebuildable(Chart: TXLSXChart): Boolean;
var
  I, T: Integer;
  S: TXLSXChartSeries;
begin
  Result := False;
  for I := 0 to Chart.SeriesCount - 1 do    // her çizim grubunu kapsar
  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      // sonraki kaydetme bu grafiği modelden yeniden kurar
else
  Log('kept original ChartML for this chart');

AddPlotGroup ve ikincil eksenle kombinasyon grafikleri

TXLSXChart.AddPlotGroup, kendi grafik ailesine ve kendi birincil ya da ikincil eksen atamasına sahip, sıralı bir çizim grubu ekler. Bu, her finans ekibinin istediği grafiği kapsar: gelir sol eksende sütunlar, marj yüzdesi sağda bir çizgi. Her grafik ValueAxis, CategoryAxis, SecondaryValueAxis, SecondaryCategoryAxis ve SeriesAxis'i açar; konum, eksen tanımlayıcıları, kesişim, tarih birimleri, etiket aralıkları, sayı biçimi ve görünürlük için tipleştirilmiş ayarlarla

Tek türden grafiklerin düz API'si hâlâ oradadır ve gruplara eşlenir. ChartType ile AddSeries ilk çizim grubuna hitap eder; SeriesCount ile Series[], her grubun serilerini sırayla gezer; yukarıdaki yardımcının çizgi serisini de görmesinin nedeni budur. Sıranın önemsediği tek yer vardır. TXLSXCharts.Add, hiç çizim grubu olmayan bir grafik kurar ve AddSeries'in ya da AddPlotGroup'un hangisi önce koşarsa birinci grubu o kurar. Taze bir grafikte önce AddPlotGroup(xlsxChartLine, xlsxAxisSecondary) çağırın: çizgi birinci grup olur, grafik türü çizgiye döner ve sonradan eklediğiniz gelir serisi ona düşer. İki grubu da açıkça kurmak belirsizliği kaldırır; örnek tam olarak bunu yapar

var
  Chart: TXLSXChart;
  Columns, Line: TXLSXChartPlotGroup;
begin
  // 1..15 satırları, 5..12 sütunları üzerine demirli; henüz çizim grubu yok
  Chart := Sheet.Charts.Add(xlsxChartColumn, 'Revenue and margin',
    1, 5, 15, 12);
  Columns := Chart.AddPlotGroup(xlsxChartColumn);              // grup 1, birincil
  Columns.AddSeries('Revenue', 'Data!$A$2:$A$13', 'Data!$B$2:$B$13');

  Line := Chart.AddPlotGroup(xlsxChartLine, xlsxAxisSecondary); // grup 2
  Line.AddSeries('Margin %', 'Data!$A$2:$A$13', 'Data!$C$2:$C$13');
  Line.ZOrder := 1;                     // çizgiyi sütunların önüne çiz

  Chart.SecondaryValueAxis.NumberFormat := '0%';
  Chart.SecondaryValueAxis.NumberFormatSourceLinked := False;
  Chart.SecondaryValueAxis.MinValue := 0;
  Chart.SecondaryValueAxis.MaxValue := 0.5;

  // Grafik alanının grafik içindeki oransal konumu: x, y, genişlik, yükseklik tek çağrıda
  Chart.PlotAreaLayout.SetPositionAndSize(0.08, 0.12, 0.80, 0.72);
end;
Delphi'de bir HotXLS kombinasyon grafiği şeması: birincil değer ekseninde AddSeries'ten gelir sütunları, ikincil değer ekseninde AddPlotGroup'tan marj çizgisi; ChartType ile AddSeries yalnızca birinci gruba ulaşır
Her AddPlotGroup çağrısı, kendi grafik ailesiyle ve birincil ya da ikincil eksen atamasıyla sıralı bir grup ekler; ChartType ile AddSeries hâlâ birinci gruba hitap eder

Çizim grubundaki ZOrder, katmanlamayı denetler: grafik yazıcısı, c:barChart ile c:lineChart bloklarını yaymadan önce grupları ona göre sıralar; küçük değerler arkada kalır. Sıralı her grup modelde olduğu için bir kombinasyon grafiğini açıp yeniden kaydetmek, her grafik ailesini ikincil, tarih ve seri eksenleriyle birlikte tutar. Grafik formül başvuruları ve önbelleklenmiş seri değerleri çalışma kitabı kopyalarında ve aç-kaydet döngülerinde yaşar; grafik demirleri, grafik parçalarıyla ilişki tanımlayıcısından eşlenir, böylece ilişki girdilerini başka bir aracın yeniden sıraladığı bir paket bile her grafiği kendi çalışma sayfası konumuna koyar

Office elle yapılmış bir düzeni neden yok sayar?

Çünkü koordinatlar eksik gelmiştir. Office, x, y, w ve h'den herhangi biri bir c:manualLayout içinde mevcutken dördünü eksiksiz bir grup olarak ister; aksi halde bütün öğeyi yok sayar. Şema dördünü de isteğe bağlı sayar; dolayısıyla yalnızca x ile y taşıyan bir dosya geçerlidir ve Excel'de yine de hiçbir şey yapmaz

TXLSXChartLayout.SetPositionAndSize kullanılacak çağrıdır. Dört değeri ayarlar, dört mod öğesini var olarak işaretler ve düzeni tek adımda etkinleştirir. Tekil X, Y, Width ve Height özellikleri birer değer ayarlar ve HotXLS tam olarak var olan öğeleri yazar; bir dosyayı okurken dokuz isteğe bağlı çocuğun varlığını koruduğu gibi. Bu, dokunulmamış bir düzenin kaynağına sadık kalmasını sağlar. Aynı zamanda iki özellik atamasından kurulan bir düzenin iki koordinatla yazılacağı ve Office'in onu atlayacağı anlamına gelir. Aynı TXLSXChartLayout sınıfı PlotAreaLayout, TitleLayout, LegendLayout ve her eksenin TitleLayout'unun arkasındadır

HotXLS elle düzen kuralının Delphi'deki şeması: x, y, w ve h eksiksiz bir grup olarak gelmezse Office bütün manualLayout öğesini yok sayar; kenar ve çarpan kipleriyle layoutTarget yalnızca grafik alanıyla sınırlıdır
Office elle yapılmış bir düzeni yalnızca x, y, w ve h eksiksiz bir grup olarak geldiğinde uygular; SetPositionAndSize dördünü de tek çağrıda yazdığı için

İki varsayılan, spesifikasyonun izin verdiğinin tamamını değil, Excel'in yaptığını izler. edge'i XMode ile YMode için, factor'ü WidthMode ile HeightMode için kullanır; standart öteki kombinasyonlara izin verir ama Excel konumları kenar, boyutları çarpan olarak kaydeder ve kaydederken ötekini dönüştürür. layoutTarget (Target, iç ya da dış) grafik alanı için yazılır; başlık, eksen başlığı ve gösterge belgesi düzenleri onu bırakır, çünkü Microsoft'un grafik şeması için uygulama notları, o öğeler altında buna izin vermez

Bir çalışma kitabı gidiş-dönüşüne oturtmak

Grafik korunumu daha geniş bir kuralın tek örneğidir: başka bir yerde yazılmış belgeleri düzenleyen bir kütüphane, kendisinden isteneni değiştirmeli ve gerisini bulduğu gibi bırakmalıdır. HotXLS aynı akıl yürütmenin temalar, hesaplama zincirleri ve çalışma sayfası uzantılarına uygulanmış halini tema, extLst ve calcChain için kayıpsız gidiş-dönüşler yazısında, VBA projeleri ve dış bağlantılara uygulanmış halini ise VBA ve dış bağlantıların korunumu yazısında ele alır. Sıfırdan grafik kurmak, resim yerleştirmek ve şekil çizmek için HotXLS'te grafikler, resimler ve çizimler yazısına bakın

HotXLS, XLSX grafiklerini makinede Excel olmadan yerel Delphi ve C++Builder kodundan okur, düzenler ve yazar; grafik sadakati bir otomasyon sunucusuna değil kütüphaneye karar verir. Grafik API referansı ve deneme sürümü HotXLS Delphi elektronik tablo bileşeni sayfasındadır