Teknik Makale

HotXLS Delphi Component: ODS open, save, and round-trip in Delphi

Yıllardır .xlsx üreten bir Delphi raporlama arka ucu yeni bir gereksinim alır: kamu sektöründen bir müşterinin satın alma kuralları OpenDocument Spreadsheet çıktısını zorunlu kılar ve o hesaptaki analistler düzenlemelerini LibreOffice'ten kaydedilmiş .ods dosyaları olarak geri gönderir. Şimdi aynı kodun ODS'yi hem yazması hem okuması gerekiyor. losLab'ın Delphi ve C++Builder için yerel Object Pascal elektronik tablo kütüphanesi olan HotXLS, hiçbir yerde Excel ya da LibreOffice kurulu olmadan her iki yönü de ele alır. İki yön birbirine yakın ama aynı değil. HotXLS 2.88.229'dan bu yana içe aktarma yolu, formülleri önbelleğe alınmış sonuçlarının yanında canlı formüller olarak geri getirir ve güncel sürümler stilleri, sayı biçimlerini, birleştirilmiş aralıkları, yorumları, görüntüleri, grafikleri ve koşullu biçimleri de okur. Hâlâ hayatta kalmayan şey, tam OpenFormula semantiği ve bir avuç paket düzeyi ayrıntıdır; bu yüzden kusursuz bir ayna varsayan bir ekip, müşterinin revizyonu ile bir sonraki rapor arasında bir yerde, çoğunlukla işaret edecek hiçbir hata olmadan uç durumlarla yine karşılaşır

ODS desteği XLS cephesinde değil, XLSX cephesinde yaşar

HotXLS tek bir paket içinde iki bağımsız sınıf hiyerarşisi gönderir: ikili BIFF8 .xls dosyaları için lxHandle biriminde TXLSWorkbook ve OOXML .xlsx paketleri için lxHandleX biriminde TXLSXWorkbook. Her OpenDocument giriş noktası - OpenODS, SaveAsODS, GetODSSheetNames - TXLSXWorkbook'a asılıdır. Bu yerleşim keyfi değildir. OASIS ODF 1.3'te belirtildiği gibi bir ODS paketi, bir mimetype üyesi, bir manifest ve bir content.xml gövdesi taşıyan bir zip arşividir, ki bu onu OOXML zip'inin yapısal bir kuzeni yapar; BIFF8 ise ortak hiçbir yanı olmayan 1990'lardan kalma ikili bir kayıt akışıdır

Bu yerleşimin pratik bir keskin kenarı var: eski bir .xls çalışma kitabı tek bir çağrıda .ods olamaz. BIFF içeriğini önce lxXlsxExport biriminden SaveXLSWorkbookAsXLSX ile XLSX modeline köprülersiniz, sonucu TXLSXWorkbook aracılığıyla yeniden açarsınız, ardından oradan dışa aktarırsınız. Köprü kayıpsız değildir ve üzerine inşa etmeden önce boşlukları bilmeye değer. Değerleri, formülleri, sayı biçimlerini, fontları, dolguları, kenarlıkları, birleştirilmiş aralıkları, sütun genişliklerini ve satır yüksekliklerini kopyalar. Yorumları, grafikleri ve koşullu biçimlendirmeyi düşürür. Ağır biçimlendirmeli bir .xls kaynağı, ODS'ye ayrıldığından daha sade görünerek ulaşır ve bu, ODS yazıcısının değil, köprünün bir özelliğidir

İçe aktarma tarafında tespit otomatiktir. Düz Open metodu bir ODS paketini mimetype üyesinden tanır, bu üye yoksa üst düzey bir content.xml kontrolüne geri döner, bu yüzden genel bir "kullanıcının ne yüklediğini aç" kod yolunun kendi uzantı koklamasına ihtiyacı yoktur. Açıldıktan sonra, SourceFormat özelliği hangi dalın tetiklendiğini bildirir

Delphi'de HotXLS sınıf düzeninin şeması: her ODS giriş noktası TXLSXWorkbook üzerinde yaşar ve bir SaveXLSWorkbookAsXLSX köprüsü BIFF8 .xls içeriğini karşıya taşır
Her OpenDocument giriş noktası TXLSXWorkbook'a asılır ve eski bir .xls, ODS'ye yalnızca kayıplı BIFF'den-XLSX'e köprüsü üzerinden ulaşır

TODSExportOptions ile ODS'ye dışa aktarma

Dışa aktarma çağrısının kendisi tek bir satırdır; onun etrafındaki seçenekler nesnesi, bir incelemecinin daha sonra soracağı kararları taşır:

var
  Book: TXLSXWorkbook;
  Opts: TODSExportOptions;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('quarterly-report.xlsx');
    Opts := TODSExportOptions.Create;        // çağıran buna sahiptir ve serbest bırakır
    try
      Opts.Generator := 'ReportService 4.2'; // meta:generator geçersiz kılma
      Opts.IncludeCharts := True;
      Opts.IncludeImages := True;
      Book.SaveAsODS('quarterly-report.ods', Opts);
    finally
      Opts.Free;
    end;
  finally
    Book.Free;
  end;
end;

Seçenekler nesnesine çağıran sahiptir. HotXLS onu serbest bırakmaz, bu yüzden içteki try..finally oradadır ve isteğe bağlı değildir. Çıktıyı yalnızca etiketlemek yerine değiştiren iki özelliğe daha yakından bakmaya değer. IncludeCharts := False ayarlamak grafikleri gizlemekten fazlasını yapar: grafik alt belgelerini ve manifest girdilerini paketten çıkarır, ki bu tam olarak tüketicinin onlara takılacak bir veri hattı olduğu durumda istediğiniz şeydir. Generator, aksi halde HotXLS/<version> okuyan ODF meta:generator dizesini geçersiz kılar; aşağı akış araçları desteği yönlendirmek için dosya üreticilerini parmak izi olarak tanıdığında bunu geçersiz kılın. Bunların hiçbiri geçerli değilse, seçenekler nesnesini tamamen atlayın. SaveAs(FileName, xlsxOpenDocumentSpreadsheet)'i çağırmak, varsayılanlarla SaveAsODS ile aynıdır ve ikisindeki akış aşırı yüklemeleri, paketi hiçbir geçici dosya olmadan doğrudan bir HTTP yanıtına yazmanıza izin verir

İçe aktarma yolunun okuduğu - ve hâlâ atladığı

Herhangi birine gidiş-dönüş sadakati vaat etmeden önce bu kısmı dikkatle okuyun. HotXLS'te ODS içe aktarma, yalnızca skaler değerleri ve önbelleğe alınmış formül sonuçlarını tutan hafif bir yol olarak başladı ve çoktan çok ötesine geçti. Güncel sürümler content.xml ile birlikte styles.xml ve settings.xml'i okur: skaler değerler ve önbelleğe alınmış formül sonuçları, ızgaraya genişletilmiş tekrarlanan satır ve sütunlar, birleştirilmiş aralıklar, zengin metin, köprüler, yorumlar, gömülü görüntüler ve grafikler, kalıtsal ve varsayılan hücre stilleri, sayı ve tarih biçimleri, satır yükseklikleri ve sütun genişlikleri, adlandırılmış aralıklar, donmuş bölmeler, AutoFilter aralıkları, veri doğrulamaları ve koşullu biçimler. OpenODS'in hâlâ dönüştürmediği şey, tam OpenFormula semantiği, ODS koruma anahtarları, yorum geometrisi, gelişmiş yorum biçimlendirmesi ve elektronik tablo içeriği dışındaki paket özellikleridir

Formüller en çok ısırma olasılığı olan kısımdır. Bir ODF hücresi yan yana iki şey saklar: ODF 1.3 Bölüm 4'te tanımlanan OpenFormula lehçesinde yazılmış formül ifadesi ve üreten uygulamanın onun için hesapladığı son değer. Erken sürümler yalnızca önbelleğe alınmış değeri okurdu. HotXLS 2.88.229'dan bu yana OpenODS ifadeyi de içe aktarır: of:= ya da oooc:= önekiyle yazılmış bir hücre, önbelleğe alınmış değerini korur ve normalize edilmiş bir Excel formülü alır; [.A1:.B1] gibi köşeli parantezli referanslar ve noktalı virgül argüman ayırıcıları Excel sözdizimine çevrilir. 2.384.32'den bu yana içe aktarma satır içi dizi satırlarını ({1;2|3;4}, {1,2;3,4} olur) ve ~ referans birleşim operatörünü de çevirir; 2.384.55'ten bu yana böyle bir birleşim, ikinci bir parantez çifti olmadan geri okunur. OpenFormula'yı Excel sözdizimine çevirmek hâlâ, fonksiyon kelime dağarcıkları, referans sözdizimi ve hata modelleri etrafında gerçek uç durumlar taşıyan bir lehçe dönüştürme problemidir; önbelleğe alınmış değerin yanında gelmesinin nedeni de budur: içe aktardığınız sayılar tam olarak göndericinin son gördüğü sayılardır

Etrafında tasarım yapılması gereken hata modu buna paralel olarak daraldı. 2.88.229 öncesinde, LibreOffice onu son kaydettiğinde toplamları doğru olan bir elektronik tablo, artık sabit olan doğru sayılarla içe aktarılırdı: bir girdi hücresini düzenleyin, yeniden hesaplayın, hiçbir şey kıpırdamazdı. Güncel sürümlerde formüller canlı geri gelir; geriye kalan risk, çevirmenin modellemediği bir yapıdır — bu da, önbelleğe alınmış değer hâlâ göndericinin sonucunu gösterirken, üreten uygulamadan farklı yeniden hesaplanan bir formül bırakabilir. Hattınızın güvendiği formüller için yeniden hesaplanan sonuçları içe aktarılan önbellek değerleriyle karşılaştırın ve uyuşmayan her formülü kendi iş kurallarınızdan, XLSX cephesinde başında eşittir işareti olmadan ifadeyi alan Cell.Formula aracılığıyla yeniden kurun

Kalan gidiş-dönüş boşlukları etrafında tasarım yapmak

Dışa aktarma, tam bellek içi çalışma kitabı modelinden render eder: değerler, formüller, stiller ve isterseniz grafikler ve görüntüler. Formül dışa aktarma sürüm sürüm sıkılaştırıldı. 2.384.55'ten bu yana satır içi diziler, referans birleşimleri ve mutlak referanslar OpenFormula'daki anlamlarını korur; 2.384.56'dan bu yana HotXLS formül öneklerinin ad alanlarını bildirir, böylece LibreOffice her formül hücresinde #VALUE! göstermek yerine formülleri değerlendirir; 2.384.65'ten bu yana tüm sütun ve tüm satır referansları [.A:.A] OpenFormula biçiminde yazılır, böylece Excel artık onları #NAME? olarak açmaz. Koşullu biçimler de izledi: 2.384.66'dan bu yana formül tabanlı kurallar, LibreOffice'in tanıdığı biçimde, temel hücreleriyle birlikte formula-is(...) olarak yazılır; 2.384.69'dan bu yana değer ve formül kuralları, Excel'in okuduğu tek biçim olan standart style:map koşulları olarak da yazılır; içe aktarma tarafı ise her iki biçimi de okur. Güncel bir sürümde tam bir .xlsx - .ods - .xlsx döngüsü bu nedenle değerleri, formülleri, stilleri ve koşullu biçimleri korur; hâlâ kaybedebileceği şey, bir önceki bölümde listelenen boşluklardadır

Delphi'de HotXLS ODS gidiş dönüşünün şeması: bellek içi çalışma kitabı modelinden dışa aktarma ve erken sürümlerin yalnızca değerlerle sınırladığı, HotXLS 2.88.229 öncesinde formülleri sabit olarak bırakan içe aktarma
Erken sürümler yalnızca değerleri ve önbelleğe alınmış sonuçları içe aktarırdı, dolayısıyla tam bir .xlsx - .ods - .xlsx döngüsü stilleri ve canlı formülleri düşürürdü; güncel sürümler ikisini de geri okur
Book := TXLSXWorkbook.Create;
try
  Book.Open('vendor-revision.ods');          // biçim otomatik tespit edilir
  if Book.SourceFormat = xlsxOpenDocumentSpreadsheet then
  begin
    // HotXLS 2.88.229'dan bu yana formüller, önbelleğe alınmış
    // sonuçlarının yanında canlı geri gelir; kaydetmeden önce, aşağı
    // akış hattının çevrilmiş haline güvenemediği her formülü yeniden kurun.
    Book.Sheets[0].Cells[2, 5].Formula := 'SUM(B2:D2)';
    Book.SaveAs('vendor-revision.xlsx');
  end;
finally
  Book.Free;
end;

Bundan çıkan mimari kalıp: gelen .ods dosyalarını, yerinde düzenlenecek belgeler olarak değil, veri beslemeleri olarak ele alın. Kanonik çalışma kitabını .xlsx'te tutun, müşteri revizyonlarından değerleri okuyun ve kanonik kopyadan talep üzerine taze ODS üretin. Doğrulama her iki kampa da aittir - dışa aktarılan dosyaları, referans ODF tüketicisi olan LibreOffice Calc'ta ve yıllardır ODS okuyan ama grafik ve stil desteğinin kenarlarında LibreOffice ile anlaşmayan Excel'de açın. Sayfa sayısı, bir avuç anahtar hücre ve grafik varlığı, dışa aktarma profili başına yeterli bir duman kontrolü oluşturur

Bir içe aktarmaya bağlanmadan önce bir ODS dosyasını triyaj etmek

Bir uç nokta yüklemeleri kabul ettiğinde, sayfa adlarını listelemek tam bir ayrıştırmadan çok daha ucuzdur ve yapısal sürprizleri erkenden yakalar:

Delphi'de HotXLS yükleme triyaj kapısının şeması: GetODSSheetNames, tam içe aktarma çalışmadan önce okunamayan ODS paketlerini ve eksik sayfaları reddeder
Bir GetODSSheetNames sondası tam ayrıştırmadan çok daha az maliyetlidir ve hatanın dosyayı hâlâ adlandırabildiği sırada, yeniden adlandırılmış sayfa başarısızlığını yakalar
Names := TStringList.Create;
Book := TXLSXWorkbook.Create;
try
  if Book.GetODSSheetNames('incoming.ods', Names) <= 0 then
    raise Exception.Create('not a readable ODS package');
  if Names.IndexOf('Data') < 0 then
    raise Exception.Create('revision is missing the Data sheet');
finally
  Book.Free;
  Names.Free;
end;

Dönüş kuralı insanları takılmasına neden olur: HotXLS çağrıları genellikle başarıda pozitif bir sayı veya 1 ve başarısızlıkta -1 döndürür, başarısız olduklarında listeyi temizler, bu yüzden belirli bir pozitif değerle karşılaştırmak yerine <= 0 test edin. GetODSSheetNames, çalışma kitabı örneğini ne sıfırlar ne de doldurur, bu yüzden tek bir yoklama nesnesi, gelen dosyaların tüm bir dizinini denetleyebilir. Bunun gibi yapısal kontroller, en yaygın gerçek dünya hatasını - bir analistin revizyonu geri göndermeden önce bir sayfayı yeniden adlandırması ya da silmesi - hata mesajının hâlâ dosyayı ve eksik sayfayı adlandırabildiği kapıda yakalar, üç katman derinde nil bir referans olarak yüzeye çıkmak yerine

Bunun etrafında daha geniş bir dönüştürme hattı inşa ediyorsanız, çalışma kitabı denetimi ve dönüştürme çalışma tezgahı kalıbı, bir hedef biçim seçmeden önce bir dosyanın özelliklerini nasıl envanterleyeceğinizi gösterir ve büyük çalışma kitabı performansı rehberi, toplu dışa aktarmaları makul bellek sınırları içinde tutar

HotXLS, tam kaynak koduna sahip yerel bir Delphi ve C++Builder elektronik tablo kütüphanesidir; tam özellik listesi ve lisanslama ayrıntıları HotXLS Delphi Component ürün sayfasındadır