Teknik Makale

Delphi'de Kayıpsız XLSX Çift Yönlü Döngüsü: Tema, extLst, calcChain

Delphi ve C++Builder için yerel Excel kütüphanesi olan HotXLS, kayıpsız XLSX çift yönlü döngüsü (round-trip) için tasarlanmıştır: bir çalışma kitabını açın, bir hücreyi değiştirin, kaydedin; müşterinin özel teması, bilinmeyen extLst uzantı blokları ve hesaplama zincirinin (calcChain) hepsi korunur. Üç mekanizma bunun çalışmasını sağlar: xl/theme/theme1.xml dosyasının harfi harfine önbelleğe alınması, bilinmeyen <ext> bloklarının olay tabanlı olarak yeniden serileştirilmesi ve formül çalışma kitabının her kaydedilişinde yeni, spesifikasyona uygun bir xl/calcChain.xml dosyası üretilmesi

Tüm bu üçünü motive eden senaryo ne yazık ki oldukça yaygındır. Bir faturalandırma hizmeti, müşterinin Excel'de tasarladığı bir şablonu —kurumsal renk teması, bir KPI sütunundaki küçük grafikler (sparklines), daha yeni bir Excel sürümü tarafından eklenen bir koşullu biçimlendirme kuralı— yükler, B3 hücresine bir fatura toplamı yazar ve kaydeder. Müşteri sonucu açtığında, marka renklerinin standart Office mavisine döndüğünü, küçük grafiklerin kaybolduğunu ve Excel'in dosyayı "onarmayı" teklif ettiğini görür. Kod içindeki hiçbir şey bu özelliklere dokunmamıştır. Ancak kütüphane, sadece kaydederek buna neden olmuştur

Excel dosyaları kütüphane düzenlemelerinden sonra neden biçimlendirmesini kaybeder?

Excel dosyaları kütüphane düzenlemelerinden sonra biçimlendirmesini kaybeder çünkü çoğu kütüphane dosyayı düzenlemez —onu yeniden oluşturur. Bir .xlsx paketi XML parçalarının bir ZIP arşividir: xl/workbook.xml, sayfa başına bir xl/worksheets/sheetN.xml, xl/styles.xml, xl/theme/theme1.xml, xl/calcChain.xml ve daha fazlası. Tipik bir kütüphane, açılışta bu parçaları bir nesne modeline ayrıştırır ve kaydederken bu modelden her bir parçayı yeniden üretir. Nesne modelinin temsil etmediği herhangi bir özellik —hiç ayrıştırmadığı bir tema, daha yeni bir Excel sürümünden gelen bir uzantı bloğu— bellekte barınacak yer bulamaz, bu nedenle yeniden üretilen parça bunu sessizce atlar

ECMA-376 bu sorunun yarısını öngörmüştü. SpreadsheetML, extLst (ECMA-376 Kısım 1, "Gelecek Özellik Veri Depolama Alanı", §18.2.10) yapısını belirlenmiş bir uzantı noktası olarak tanımlar: daha yeni üreticiler özellikleri orada barındırır, her biri özelliği tanımlayan bir uri özniteliği taşıyan bir <ext> öğesiyle sarmalanır ve eski tüketicilerin anlamadıkları şeyleri koruması beklenir. Küçük grafikler, dilimleyiciler (slicers) ve daha yeni koşullu biçimlendirme türlerinin hepsi bu yolla taşınır. Bilinmeyen <ext> bloklarını atan bir kütüphane bu nedenle yalnızca kayıplı olmakla kalmaz —biçimin etrafında tasarlandığı ileriye dönük uyumluluk sözleşmesini de ihlal eder. Değerlendirdiğiniz herhangi bir elektronik tablo kütüphanesine sorulacak soru nettir: Bir hücreyi değiştirirsem, başka ne değişir?

HotXLS özel bir temayı bayt bayt nasıl korur?

HotXLS, açılış anında xl/theme/theme1.xml dosyasının orijinal baytlarını önbelleğe alarak ve kaydetme anında bunları harfi harfine geri yazarak bir çalışma kitabının temasını korur. Tema parçası (ECMA-376 Kısım 1, §14.2.7) SpreadsheetML değil, DrawingML'dir —renk şemaları, yazı tipi şemaları, biçim şemaları— ve bir elektronik tablo motorunun bunu derinlemesine modellemesi için hiçbir neden yoktur. Önceki HotXLS sürümleri her kaydetmede sabit bir Office teması üretiyordu ki bu, yukarıdaki "marka renklerinin standart renklere dönmesi" hatasının tam olarak aynısıdır; v2.89.46 sürümünden bu yana açılan paketin teması ham olarak saklanır ve dokunulmadan yeniden yayılır, yerleşik Office teması ise yalnızca sıfırdan oluşturulan çalışma kitapları için üretilir. Ham baytlar olası en güçlü doğruluk garantisidir: ayrıştırma yok, yeniden serileştirme yok, sapma olasılığı yok

Harfi harfine kopyalama, programlı tema erişimine karşı bilinçli olarak kazanır. TXLSXWorkbook, yeni çalışma kitapları için başlık ve gövde yazı tiplerini seçebilmeniz amacıyla ThemeMajorFont ve ThemeMinorFont özelliklerini sunar, ancak açılışta harfi harfine bir tema yakalandığında bu belirleyicilerin kaydedilen dosya üzerinde hiçbir etkisi olmaz —çift yönlü döngü önceliklidir. Mevcut bir çalışma kitabının temasını gerçekten değiştirmeniz gerekiyorsa, bu, şablonu bir veri odaklı API yerine Excel'in kendisinde düzenlemeniz gerektiğine dair bir işarettir. Günlük kullanımda hiçbir API'ye gerek yoktur:

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('branded-invoice.xlsx');
    Book.Sheets[0].Cells[3, 2].Value := 42750.00;  // the one edit
    Book.SaveAs('branded-invoice-out.xlsx');
    // theme1.xml in the output is byte-identical to the input
  finally
    Book.Free;
  end;
end;

Bilinmeyen extLst bloklarına kaydetme sırasında ne olur?

HotXLS, yerel olarak modellemediği her çalışma sayfası düzeyindeki <ext> bloğunu yakalar Yapı ve bunu kaydedilen çalışma sayfasının extLst yapısına yeniden oynatır, böylece daha yeni Excel sürümleri tarafından yazılan özellikler çift yönlü döngüden zarar görmeden kurtulur. v2.131.0 sürümünden bu yana yakalanan parçalar, her XLSX çalışma sayfasındaki bir TStringList olan salt okunur RawWorksheetExts özelliği aracılığıyla görülebilir, bu da garantiyi bir inanç eylemi olmaktan çıkarıp test kodundan denetlenebilir kılar:

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  i: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('from-newer-excel.xlsx');
    Sheet := Book.Sheets[0];
    WriteLn(Format('%d foreign ext block(s) captured',
      [Sheet.RawWorksheetExts.Count]));
    for i := 0 to Sheet.RawWorksheetExts.Count - 1 do
      WriteLn(Copy(Sheet.RawWorksheetExts[i], 1, 100)); // peek at each uri
  finally
    Book.Free;
  end;
end;

Bilinmesi gereken uygulama detayı, yakalamanın ham bir bayt kopyası değil, olay düzeyinde bir yeniden serileştirme olduğudur. HotXLS'in akışlı XML okuyucusu hiçbir kaynak ofseti sunmaz, bu nedenle bilinmeyen alt ağaç, akıştan geçen Element, Text ve EndElement olaylarından yeniden oluşturulur. Bu yaklaşım klasik bir tuzağı gizler: <a/> gibi kendi kendini kapatan bir öğe, yalnızca boş olarak flagged bir Element olayı tetikler ve hiçbir zaman EndElement tetiklemez, bu nedenle yalnızca EndElement üzerinde azalan herhangi bir derinlik sayacı alt ağacın kapandığını asla göremez. Bu durumu ele aldığınızda, yeniden oluşturulan parça orijinaliyle semantik olarak eşdeğerdir —öznitelik tırnak içine alma ve kendi kendini kapatan biçimler normalize edilir, bu nedenle bayt bayt aynı değildir ancak Excel baytları değil, anlamı okur. Excel'in kendi çıktısının iki özelliği yeniden oynatmayı güvenli kılar: Excel gerekli xmlns özniteliklerini <ext> öğesi üzerinde veya içinde bildirir, bu nedenle yakalanan her parça ad alanı açısından kendi kendine yeterlidir ve bu kendi kendine yeterlilik, bir çalışma sayfasını çalışma kitapları içinde veya arasında kopyalarken yabancı blokları düz bir dize listesi atamasıyla birlikte taşıyabilmenin nedenidir

Excel'in formüllerinize güvenmesi için calcChain.xml yazma

HotXLS, kaydedilen çalışma kitabı formüller içerdiğinde xl/calcChain.xml (Hesaplama Zinciri parçası, ECMA-376 Kısım 1, §12.3.1) yazar ve iki sıralama arasında seçim yapar. Formül bağımlılık grafiği zaten oluşturulmuş ve güncelse —son düzenlemenizden sonra Recalculate çağırdıysanız— zincir tam topolojik sırada, bağımlılıklar dependent'lardan önce olacak şekilde yayılır ve herhangi bir dairesel referans üyesi sona eklenir. Aksi takdirde hücreler belge sırasına göre listelenir. Her ikisi de doğrudur: Microsoft'un biçim için uygulama notları [MS-XLSX], hesaplama zincirini Excel'in yükleme sırasında doğruladığı ve yeniden sıraladığı bir ipucu olarak kabul eder, bu nedenle herhangi bir eksiksiz listeleme yasal kabul edilir ve HotXLS, SaveAs içinde bir grafik oluşturmayı kasıtlı olarak reddeder —kenar oluşturma hücre sayısına göre kareseldir, bu da milyon hücrelik bir kaydetmede kabul edilemez bir gizli maliyetidir

Book.Open('model.xlsx');
Book.Sheets[0].Cells[10, 4].Formula := '=SUM(D2:D9)';
// Saved now, calcChain.xml lists formula cells in document order.
// After Recalculate the dependency graph exists, so the same save
// emits a full topological order instead:
Book.Recalculate;
Book.SaveAs('model-out.xlsx');

Excel'in danışmanlık olarak gördüğü bir parçayı neden önemseyelim? Çünkü onun yokluğu bir sinyaldir. Bazı tüketiciler —onarım sezgisel yöntemleri, üçüncü taraf görüntüleyiciler, diff araçları— bir formül çalışma kitabının bir hesaplama zinciri taşımasını bekler ve kaydederken bu parçayı sessizce atan bir kütüphane, Excel'in yazdıklarından biraz farklı dosyalar üretir. Geçerli bir zincir yaymak, çıktıyı ekosistemin geri kalanının test edildiği zarfın içinde tutar ki bu da çift yönlü döngü mühendisliğinin sessiz ve gösterişsiz özüdür

Kayıpsız çift yönlü döngünün bittiği yer

Dürüstlük burada bir pazarlama onay kutusundan daha önemlidir, bu nedenle sınırları da aynı şekilde ele almak gerekir. HotXLS paketin tamamını bayt bayt kopyalamaz: çalışma sayfası XML'i, stiller, paylaşılan dizeler ve çalışma kitabı parçaları ayrıştırılmış modelden yeniden üretilir, bu nedenle çıktı semantik olarak aslına uygundur ancak ikili düzeyde aynı değildir —yalnızca ZIP yerel başlıkları bile taze DOS zaman damgaları taşır. Yakalanan <ext> parçaları yukarıda açıklandığı gibi normalize edilmiş olarak geri döner. Harici bir tema mevcut olduğunda programlı tema yazı tipi geçersiz kılmaları yoksayılır. Ve koruma ağının tanımlanmış bir göz genişliği vardır: HotXLS'in yerel olarak modellediği özellikler (örneğin, küçük grafikler körü körüne kopyalanmak yerine ayrıştırılır ve yeniden yazılır) artı yabancı extLst içeriği artı harici olarak önbelleğe alınmış parçalar. Ne modellenen ne de bir uzantı noktası içinde yer alan bir parça —örneğin, egzotik bir eklentinin özel parçası— bu makalenin kapsadığı üç mekanizmanın dışında kalır, bu nedenle inançla hareket etmek yerine gerçek şablonlarınızı test edin

Bitişik koruma çalışmaları tabloyu tamamlar. VBA projeleri ve harici çalışma kitabı başvuruları, VBA ve harici bağlantı koruması hakkındaki tamamlayıcı makalede ele alınan modellemediğin-her-şeyi-koru felsefesiyle kaydetme sürecinden geçer ve docProps içindeki belge özellikleri, sessizce atılmak yerine kendi okuma-yazma API'sine sahiptir. Herhangi bir elektronik tablo kütüphanesini değerlendirirken, tek hücre testini çalıştırın: zengin özellikli bir üretim çalışma kitabını açın, tek bir değeri değiştirin, kaydedin ve açılan parçaları orijinaliyle karşılaştırın. Dokunduğunuz sayfanın ötesinde nelerin değiştiği, size kütüphane hakkında herhangi bir özellik matrisinden daha fazlasını söyler

Burada açıklanan çift yönlü döngü mekanizmaları —v2.89.46'dan beri harici tema koruması, v2.131.0'dan beri yabancı extLst yakalama ve calcChain.xml emisyonu— ürün sayfası Delphi ve C++Builder için tam XLSX okuma-yazma özellik setini belgeleyen güncel HotXLS Delphi Excel Bileşeni'nde sunulmaktadır